Compare commits

...

30 Commits

Author SHA1 Message Date
ef4dfb0307 获取邀请人信息和扫一扫加入xxx家庭 2026-06-22 16:50:18 +08:00
086f1226a1 优化推荐吗页面按钮 2026-06-21 09:12:33 +08:00
5cf30411c1 优化某某某家庭页面 2026-06-20 16:27:19 +08:00
76e4949dd7 移除页面无用功能 2026-06-20 16:22:07 +08:00
4a16010bd6 某某某家庭 2026-06-20 16:19:59 +08:00
b55d9a4b38 修改全局按钮背景阴影,生成二维码和撤销接口调用 2026-06-20 16:11:09 +08:00
6a1cfaeda9 修改按钮 2026-06-20 15:03:29 +08:00
f57975ef1e 修改base_url地址,斌一起当前版本适配 2026-06-19 08:40:56 +08:00
548d501a44 列表网络错误显示 2026-06-18 09:02:21 +08:00
a997190480 优化列表图片展示 2026-06-16 15:59:11 +08:00
631f9831c3 优化列表请求失败和没有网络的显示状态,优化渐变颜色展示 2026-06-16 15:40:20 +08:00
b2c3ed9015 退出登录 2026-06-15 16:02:24 +08:00
6bd31327db 优化家庭状态 2026-06-15 14:06:13 +08:00
7e362038b3 优化列表 2026-06-15 10:19:42 +08:00
d30c025874 注释解释 2026-06-12 17:20:31 +08:00
78d2d2cd79 下载视频 2026-06-11 15:25:06 +08:00
9eb47345fe 修改包名 2026-06-11 14:25:09 +08:00
57188d27cc 视频下载功能 2026-06-11 11:53:52 +08:00
e40403f7db 视频删除功能 2026-06-11 09:45:31 +08:00
09fdb75644 全屏直播自定义功能按钮 2026-06-10 16:01:24 +08:00
0f07736d1e 全屏视频自定义 2026-06-10 11:20:35 +08:00
e7331f64f4 扫一扫打开相册 2026-06-09 16:43:24 +08:00
98e7d57c7d 启动页修改 2026-06-09 08:45:23 +08:00
3c8ac29398 全局图标优化2 2026-06-08 17:23:25 +08:00
5f945fe6e3 全局图标优化 2026-06-08 17:07:44 +08:00
11ab4e244d 登录注册图标优化 2026-06-08 11:58:40 +08:00
5cafd2ac1d 登录启动页优化 2026-06-08 10:39:30 +08:00
c2b947c630 实时视频组件优化 2026-06-05 17:17:33 +08:00
d60322cbc2 视频组件 2026-06-05 09:33:30 +08:00
88c791a520 视频组件更换StandardGSYVideoPlayer 2026-06-04 17:51:19 +08:00
469 changed files with 2989 additions and 1929 deletions

View File

@@ -13,6 +13,17 @@
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="AlarmLogActivity">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-05-18T09:18:19.023586Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=80bece4" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker7700090853507788345.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker12680940025563006602.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -0,0 +1,48 @@
kotlin version: 2.2.10
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker3894271637508823813.tmp
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:30)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View File

@@ -3,7 +3,7 @@ plugins {
}
android {
namespace = "com.example.defenseapplication"
namespace = "com.ckkj.defense_device"
compileSdk {
version = release(36) {
minorApiLevel = 1
@@ -11,9 +11,9 @@ android {
}
defaultConfig {
applicationId = "com.example.defenseapplication"
applicationId = "com.ckkj.defense_device"
minSdk = 24
targetSdk = 36
targetSdk = 30
versionCode = 1
versionName = "1.0"
@@ -101,8 +101,10 @@ dependencies {
implementation("com.yanzhenjie.recyclerview:x:1.3.2")
//Android-PickerView-时间弹窗
implementation("com.contrarywind:Android-PickerView:4.1.9")
//基于exoPlayer 自定义播放器 JPlayer支持功能
implementation("com.ycjiang:VideoPlayModule-Lite:2.3.61")
// GSYVideoPlayer
implementation("io.github.carguo:gsyvideoplayer:13.0.0")
//下载
implementation("io.github.chiclaim:downloader:1.0.2")
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication
package com.ckkj.defense_device
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

View File

@@ -22,17 +22,30 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/loge"
android:icon="@mipmap/logo"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.DefenseApplication">
<activity
android:name=".home.StartActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".personal.LoginPasswordActivity"
android:exported="false" />
<activity
android:name=".personal.InvitationCodeActivity"
android:exported="false" />
<activity
android:name=".personal.JoinFamilyInviteActivity"
android:exported="false" />
<activity
android:name=".liveVideo.StrikePositionActivity"
android:exported="false" />
@@ -90,6 +103,7 @@
android:exported="false" />
<activity
android:name=".liveVideo.LiveVideoActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:exported="false" />
<activity
android:name=".home.SetNameActivity"
@@ -108,7 +122,8 @@
android:exported="false" />
<activity
android:name=".home.HomeActivity"
android:exported="false" />
android:exported="false">
</activity>
<activity
android:name=".login.RegisterSuccessActivity"
android:exported="false" />
@@ -126,13 +141,7 @@
android:exported="false" />
<activity
android:name=".login.LoginActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
android:exported="false" />
<provider
android:name="androidx.core.content.FileProvider"

View File

@@ -1,25 +1,15 @@
package com.example.defenseapplication
package com.ckkj.defense_device
import android.app.Application
import android.content.Context
import bean.LoginBean
import com.blankj.utilcode.util.Utils
import com.example.defenseapplication.bean.BaseInfo
import com.example.defenseapplication.utils.ApiConfig
import com.example.defenseapplication.utils.LanguageUtil
import com.example.defenseapplication.utils.UserinfoUtil
import com.google.gson.Gson
import com.ckkj.defense_device.utils.LanguageUtil
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
Utils.init(this) // 初始化 AndroidUtilCode
var token = UserinfoUtil.getUserInfo()?.access_token;
if (token != null) {
ApiConfig.authToken = token
};
LanguageUtil.init(this)
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.View
@@ -6,7 +6,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.ckkj.defense_device.R
data class Device(
val name: String,
@@ -18,6 +18,8 @@ class AddDeviceAdapter(
private val onItemClick: (Device) -> Unit
) : RecyclerView.Adapter<AddDeviceAdapter.ViewHolder>() {
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val ivIcon: ImageView = itemView.findViewById(R.id.ivDeviceIcon2)
val tvName: TextView = itemView.findViewById(R.id.tvDeviceName2)
@@ -47,7 +49,9 @@ class AddDeviceAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val device = devices[position]
holder.tvName.text = device.name
holder.ivIcon.setImageResource(R.drawable.img)
val iconResId = defaultIcons[position % defaultIcons.size]
holder.ivIcon.setImageResource(iconResId)
// 点击事件
holder.itemView.setOnClickListener { onItemClick(device) }

View File

@@ -1,11 +1,10 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.graphics.Color
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ItemAddLinkedUserBinding
import com.ckkj.defense_device.databinding.ItemAddLinkedUserBinding
data class UserItem(val name: String, val phone: String, val userId: String = "")

View File

@@ -1,10 +1,10 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.bean.DeviceAlarmBean
import com.example.defenseapplication.databinding.ItemAlarmLogBinding
import com.ckkj.defense_device.bean.DeviceAlarmBean
import com.ckkj.defense_device.databinding.ItemAlarmLogBinding
class AlarmLogAdapter(
private val items: List<DeviceAlarmBean>,

View File

@@ -1,11 +1,11 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.defenseapplication.bean.DeviceAlarmBean
import com.example.defenseapplication.databinding.ItemAlarmLogHorizontalBinding
import com.ckkj.defense_device.bean.DeviceAlarmBean
import com.ckkj.defense_device.databinding.ItemAlarmLogHorizontalBinding
class AlarmLogHorizontalAdapter(
private val items: List<DeviceAlarmBean>,

View File

@@ -1,27 +1,20 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ItemDeviceBinding
data class DeviceUi(
val name: String,
val status: String,
val deviceId: String = ""
) {
val isOnline: Boolean
get() = status == "1"
}
import com.bumptech.glide.Glide
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.ItemDeviceBinding
class DeviceAdapter(
private val onItemClick: (DeviceUi) -> Unit = {}
private val onItemClick: (DeviceBean) -> Unit = {}
) : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
private val items = mutableListOf<DeviceUi>()
private val items = mutableListOf<DeviceBean>()
fun submitList(deviceList: List<DeviceUi>) {
fun submitList(deviceList: List<DeviceBean>) {
items.clear()
items.addAll(deviceList)
notifyDataSetChanged()
@@ -33,15 +26,25 @@ class DeviceAdapter(
}
override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) {
holder.bind(items[position])
holder.bind(items[position], position)
}
override fun getItemCount(): Int = items.size
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
inner class DeviceViewHolder(private val binding: ItemDeviceBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: DeviceUi) {
fun bind(item: DeviceBean, position: Int) {
if (item.deviceImg.isNullOrEmpty()) {
val iconResId = defaultIcons[position % defaultIcons.size]
binding.ivDeviceIcon.setImageResource(iconResId)
} else {
Glide.with(binding.root.context)
.load(item.deviceImg)
.into(binding.ivDeviceIcon)
}
binding.tvDeviceType.text = item.name
when (item.status) {
"1" -> {

View File

@@ -1,12 +1,12 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.FamilyBean
import com.example.defenseapplication.databinding.ItemFamilyBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.FamilyBean
import com.ckkj.defense_device.databinding.ItemFamilyBinding
class FamilyAdapter(
private val onItemClick: (String) -> Unit
@@ -64,7 +64,7 @@ class FamilyAdapter(
binding.tvFamilyName.setTextColor(binding.root.context.getColor(R.color.white))
binding.tvFamilyRole.setTextColor(binding.root.context.getColor(R.color.white))
binding.ivCheck.visibility = View.VISIBLE
binding.ivCheck.setImageResource(R.drawable.ic_check_white)
binding.ivCheck.setImageResource(R.mipmap.ic_check_white)
} else {
binding.layoutFamilyItem.setBackgroundResource(0)
binding.tvFamilyName.setTextColor(binding.root.context.getColor(R.color.black))

View File

@@ -1,12 +1,12 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ItemSwitchFamilyBinding
import com.example.defenseapplication.model.FamilyItem
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ItemSwitchFamilyBinding
import com.ckkj.defense_device.model.FamilyItem
class FamilySwitchAdapter(
private var familyList: List<FamilyItem>,

View File

@@ -1,11 +1,10 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ItemLinkedUserBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ItemLinkedUserBinding
data class LinkedUser(
val userId: String,

View File

@@ -1,11 +1,11 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.databinding.ItemMessageGroupTitleBinding
import com.example.defenseapplication.databinding.ItemMessageNoticeBinding
import com.ckkj.defense_device.databinding.ItemMessageGroupTitleBinding
import com.ckkj.defense_device.databinding.ItemMessageNoticeBinding
sealed class MessageListItem {
data class Header(val title: String) : MessageListItem()

View File

@@ -1,9 +1,9 @@
package com.example.defenseapplication.adapter
package com.ckkj.defense_device.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.databinding.ItemWifiBinding
import com.ckkj.defense_device.databinding.ItemWifiBinding
class WifiListAdapter(
wifiList: List<String>,

View File

@@ -1,10 +1,10 @@
package com.example.defenseapplication.base
package com.ckkj.defense_device.base
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.utils.ActivityManager
import com.example.defenseapplication.utils.LanguageUtil
import com.ckkj.defense_device.utils.ActivityManager
import com.ckkj.defense_device.utils.LanguageUtil
open class BaseActivity : AppCompatActivity() {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class AddDeviceBean(
val deviceEm: String? = null,//设备型号

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class AddDeviceUserRequest(
val deviceId: String,

View File

@@ -1,10 +1,10 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
/**
* 统一返回数据基类根据你的后端实际字段调整
*/
data class BaseInfo(
val message: String? = null,
val msg: String? = null,
val code: Int = 0,
val data: Any? = null
)

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
class DeleteBean {
var deviceId: String? = null

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class DeleteDeviceUserRequest(
val userId: String,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
import android.os.Parcel
import android.os.Parcelable

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class DeviceAlarmResponse(
val total: Int,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class DeviceBean(
val alertAudio: String? = null,//告警播放
@@ -27,4 +27,13 @@ data class DeviceBean(
val wifiPassword: String? = null,//WiFi密码
val workMode: String? = null,//工作模式 0-自动 1-半自动
val optionPassword: String? = null, //操作密码
)
) {
val deviceId: String
get() = id ?: ""
val name: String
get() = deviceName ?: ""
val isOnline: Boolean
get() = status == "1"
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class DeviceUserBean(
val createDept: Int ? = null, //创建部门

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class FamilyBean(
val id: String,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class FamilyMembersBean(
val id: String? = null, // ID

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class GetCodeBean<T> (
val code:Int,

View File

@@ -0,0 +1,7 @@
package com.ckkj.defense_device.bean
data class JoinTheFamilyBean(
val parentId: String,
val inviteCode: String,
)

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
import android.os.Parcel
import android.os.Parcelable

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class MessageListResponse(
val total: Int,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class ObtainInviterInformationBean(
val nickName: String? = null,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class ObtainInviterInformationRequest(
val childId: String

View File

@@ -0,0 +1,8 @@
package com.ckkj.defense_device.bean
data class RecommendationCodeBean(
val inviteCode: String? = null,
val code: String? = null,
val expireTime: Long? = null,
val expire: Long? = null
)

View File

@@ -0,0 +1,5 @@
package com.ckkj.defense_device.bean
data class SelectInviterInfoBean(
val code: String
)

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class SwitchFamilyRequest(
val clientId: String,

View File

@@ -0,0 +1,8 @@
package com.ckkj.defense_device.bean
data class UpdateFamilyUserRequest(
val id: String,
val optionPassword: String,
val optionStatus: String,
val status: String,
)

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class UpdatePassword(
val oldPassword: String,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class UpdateUserInfo(
val userId: String?=null,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class UploadResponse(
val code: Int,

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
import java.io.Serializable

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.bean
package com.ckkj.defense_device.bean
data class VerifyOptionPasswordRequest(
val optionPassword: String

View File

@@ -1,7 +1,6 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
@@ -14,10 +13,10 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AddDeviceAdapter
import com.example.defenseapplication.adapter.Device
import com.example.defenseapplication.databinding.AddDeviceActivityBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.AddDeviceAdapter
import com.ckkj.defense_device.adapter.Device
import com.ckkj.defense_device.databinding.AddDeviceActivityBinding
import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.Manifest
import android.content.BroadcastReceiver
@@ -23,10 +23,10 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.WifiListAdapter
import com.example.defenseapplication.databinding.AddFamilyActivityBinding
import com.example.defenseapplication.databinding.DialogWifiListBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.WifiListAdapter
import com.ckkj.defense_device.databinding.AddFamilyActivityBinding
import com.ckkj.defense_device.databinding.DialogWifiListBinding
import com.gyf.immersionbar.ImmersionBar
//选择Wi-Fi
class AddFamilyActivity : AppCompatActivity() {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.content.Intent
import android.graphics.drawable.AnimationDrawable
@@ -6,14 +6,9 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.AddDeviceActivityBinding
import com.example.defenseapplication.databinding.ConnectDeviceActivityBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ConnectDeviceActivityBinding
import com.gyf.immersionbar.ImmersionBar
//连接设备
class ConnectDeviceActivity : AppCompatActivity() {

View File

@@ -1,17 +1,17 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import com.example.defenseapplication.R
import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.databinding.HomeActivityBinding
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
import com.gyf.immersionbar.ImmersionBar
import com.ckkj.defense_device.R
import com.ckkj.defense_device.base.BaseActivity
import com.ckkj.defense_device.databinding.HomeActivityBinding
class HomeActivity : BaseActivity() {
companion object {
const val EXTRA_REFRESH = "extra_refresh"
}
// ViewBinding 核心对象
private lateinit var binding: HomeActivityBinding
@@ -37,10 +37,10 @@ class HomeActivity : BaseActivity() {
binding.tabHome.setOnClickListener {
switchFragment(homeMenuFragment)
// 切换图标 + 文字颜色
binding.ivHome.setImageResource(R.drawable.home_checked)
binding.ivHome.setImageResource(R.mipmap.home_checked)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_selected))
binding.ivProfile.setImageResource(R.drawable.profile)
binding.ivProfile.setImageResource(R.mipmap.profile)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_normal))
}
@@ -48,12 +48,34 @@ class HomeActivity : BaseActivity() {
binding.tabProfile.setOnClickListener {
switchFragment(personalFragment)
// 切换图标 + 文字颜色
binding.ivHome.setImageResource(R.drawable.home)
binding.ivHome.setImageResource(R.mipmap.home)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_normal))
binding.ivProfile.setImageResource(R.drawable.profile_checked)
binding.ivProfile.setImageResource(R.mipmap.profile_checked)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_selected))
}
handleRefreshIntent(intent)
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
setIntent(intent)
handleRefreshIntent(intent)
}
}
private fun handleRefreshIntent(intent: Intent) {
if (intent.getBooleanExtra(EXTRA_REFRESH, false)) {
intent.removeExtra(EXTRA_REFRESH)
switchFragment(homeMenuFragment)
binding.ivHome.setImageResource(R.mipmap.home_checked)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_selected))
binding.ivProfile.setImageResource(R.mipmap.profile)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_normal))
homeMenuFragment.refreshHomeData()
}
}
/**

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.Manifest
import android.app.Activity
@@ -21,23 +21,25 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.DeviceAdapter
import com.example.defenseapplication.adapter.DeviceUi
import com.example.defenseapplication.adapter.FamilyAdapter
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.bean.FamilyBean
import com.example.defenseapplication.bean.SwitchFamilyRequest
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
import com.example.defenseapplication.liveVideo.LiveVideoActivity
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.DeviceUpdateEvent
import com.example.defenseapplication.utils.FamilySwitchEvent
import com.example.defenseapplication.utils.MessageStatusManager
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.utils.UserinfoUtil
import com.example.defenseapplication.view.DefenseDialogs
import com.example.defenseapplication.view.showDefenseDialog
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.DeviceAdapter
import com.ckkj.defense_device.adapter.FamilyAdapter
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.bean.FamilyBean
import com.ckkj.defense_device.bean.SwitchFamilyRequest
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.databinding.HomeMenuFragmentBinding
import com.ckkj.defense_device.liveVideo.LiveVideoActivity
import com.ckkj.defense_device.personal.JoinFamilyInviteActivity
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUpdateEvent
import com.ckkj.defense_device.utils.FamilySwitchEvent
import com.ckkj.defense_device.utils.MessageStatusManager
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.view.DefenseDialogs
import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
@@ -45,6 +47,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
//主页碎片
class HomeMenuFragment : Fragment() {
// ViewBinding
@@ -64,13 +67,13 @@ class HomeMenuFragment : Fragment() {
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
} else {
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
}
if (cameraGranted && storageGranted) {
startScan()
} else {
@@ -83,7 +86,7 @@ class HomeMenuFragment : Fragment() {
requireContext(),
Manifest.permission.CAMERA
)
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
requireContext(),
@@ -96,7 +99,7 @@ class HomeMenuFragment : Fragment() {
)
}
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
storagePermission == PackageManager.PERMISSION_GRANTED) {
startScan()
} else {
@@ -122,23 +125,86 @@ class HomeMenuFragment : Fragment() {
MNScanManager.RESULT_SUCCESS -> {
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
if (!result.isNullOrEmpty()) {
ToastUtils.showToast(requireContext(), "扫码成功: $result")
val inviteCode = parseInviteCode(result[0])
if (inviteCode.isNullOrEmpty()) {
Toast.makeText(requireContext(), "无效的邀请码", Toast.LENGTH_SHORT).show()
} else {
fetchInviterInfo(inviteCode)
}
} else {
ToastUtils.showToast(requireContext(), "扫码结果为空")
Toast.makeText(requireContext(), "扫码结果为空", Toast.LENGTH_SHORT).show()
}
}
MNScanManager.RESULT_FAIL -> {
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
ToastUtils.showToast(requireContext(), "扫码失败: $error")
Toast.makeText(requireContext(), "扫码失败: $error", Toast.LENGTH_SHORT).show()
}
MNScanManager.RESULT_CANCLE -> {
ToastUtils.showToast(requireContext(), "扫码取消")
Toast.makeText(requireContext(), "扫码取消", Toast.LENGTH_SHORT).show()
}
}
}
})
}
private fun parseInviteCode(scanResult: String): String? {
val trimmed = scanResult.trim()
if (trimmed.isEmpty()) return null
return try {
if (trimmed.startsWith("{")) {
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
} else {
trimmed
}
} catch (e: Exception) {
trimmed
}
}
private fun fetchInviterInfo(inviteCode: String) {
val loadingDialog = LoadingDialog(requireActivity()).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
if (response.code == 200 && response.data != null) {
val inviter = response.data
val parentId = inviter.resolveParentId()
if (parentId.isNullOrEmpty()) {
Toast.makeText(
requireContext(),
getString(R.string.network_error),
Toast.LENGTH_SHORT
).show()
return@withContext
}
val intent = Intent(requireContext(), JoinFamilyInviteActivity::class.java).apply {
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
}
startActivity(intent)
} else {
Toast.makeText(
requireContext(),
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private val unreadCountChangeListener = object : MessageStatusManager.UnreadCountChangeListener {
override fun onUnreadCountChanged(count: Int) {
updateUnreadCountUI(count)
@@ -200,10 +266,10 @@ class HomeMenuFragment : Fragment() {
// 为 LinearLayout 设置渐变背景
setGradientBackground(
view = binding.gradientLayout,
startColor = Color.parseColor("#EBDCED"),
endColor = Color.parseColor("#CAF4FF")
startColor = resources.getColor(R.color.gradientLayout1),
endColor = resources.getColor(R.color.gradientLayout2)
)
binding.message.setOnClickListener {
binding.message.setOnClickListener {
val intent = Intent(requireContext(), MessageActivity::class.java)
startActivity(intent)
}
@@ -285,6 +351,10 @@ class HomeMenuFragment : Fragment() {
}
}
fun refreshHomeData() {
fetchFamilyList()
}
private fun fetchFamilyList() {
CoroutineScope(Dispatchers.IO).launch {
try {
@@ -321,18 +391,22 @@ class HomeMenuFragment : Fragment() {
Log.d("HomeMenuFragment", "canViewDevice = $canViewDevice")
if (canViewDevice) {
binding.tvDeviceTitle.visibility = View.VISIBLE
binding.swipeRefreshLayout.visibility = View.VISIBLE
binding.rvDeviceList.visibility = View.VISIBLE
binding.message.visibility = View.VISIBLE
binding.addFamily.visibility = View.VISIBLE
binding.add.visibility = View.VISIBLE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
} else {
binding.tvDeviceTitle.visibility = View.GONE
binding.swipeRefreshLayout.visibility = View.GONE
binding.rvDeviceList.visibility = View.GONE
binding.message.visibility = View.GONE
binding.addFamily.visibility = View.GONE
binding.add.visibility = View.GONE
binding.layoutNoPermission.visibility = View.VISIBLE
binding.layoutNoNetwork.visibility = View.GONE
}
}
@@ -390,9 +464,12 @@ class HomeMenuFragment : Fragment() {
hasMore = newDevices.size >= pageSize
}
Log.d("DeviceList", "deviceBeanList size = ${deviceBeanList.size}")
showDeviceContent()
updateDeviceList()
} else {
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
noNetwork()
val errorMsg = response.msg.ifBlank { getString(R.string.network_error) }
Toast.makeText(requireContext(), errorMsg, Toast.LENGTH_SHORT).show()
if (isLoadMore) {
hasMore = false
}
@@ -401,6 +478,7 @@ class HomeMenuFragment : Fragment() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
noNetwork()
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
isLoading = false
if (isLoadMore) {
@@ -445,21 +523,31 @@ class HomeMenuFragment : Fragment() {
}
}
private fun updateDeviceList() {
val deviceUiList = deviceBeanList.map {
DeviceUi(it.deviceName ?: "", it.status ?: "", it.id ?: "")
}
private fun showDeviceContent() {
binding.swipeRefreshLayout.visibility = View.VISIBLE
binding.rvDeviceList.visibility = View.VISIBLE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
}
if (deviceUiList.isEmpty()) {
private fun noNetwork() {
binding.swipeRefreshLayout.visibility = View.GONE
binding.rvDeviceList.visibility = View.GONE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
}
private fun updateDeviceList() {
if (deviceBeanList.isEmpty()) {
deviceAdapter.submitList(listOf(
DeviceUi(getString(R.string.no_device), "0")
DeviceBean(deviceName = getString(R.string.no_device), status = "0")
))
} else {
deviceAdapter.submitList(deviceUiList)
deviceAdapter.submitList(deviceBeanList)
}
}
private fun onDeviceClicked(device: DeviceUi) {
private fun onDeviceClicked(device: DeviceBean) {
if (device.isOnline) {
DeviceIdEvent.setDeviceId(device.deviceId)
val intent = Intent(requireContext(), LiveVideoActivity::class.java)

View File

@@ -1,15 +1,15 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.adapter.MessageListAdapter
import com.example.defenseapplication.adapter.MessageListItem
import com.example.defenseapplication.databinding.MessageActivityBinding
import com.example.defenseapplication.utils.MessageStatusManager
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.adapter.MessageListAdapter
import com.ckkj.defense_device.adapter.MessageListItem
import com.ckkj.defense_device.databinding.MessageActivityBinding
import com.ckkj.defense_device.utils.MessageStatusManager
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -24,7 +24,7 @@ class MessageActivity : AppCompatActivity() {
private val pageSize = 10
private var isLoading = false
private var hasMoreData = true
private val allMessages = mutableListOf<com.example.defenseapplication.bean.MessageBean>()
private val allMessages = mutableListOf<com.ckkj.defense_device.bean.MessageBean>()
private var lastLoadedCount = 0
override fun onCreate(savedInstanceState: Bundle?) {
@@ -125,7 +125,7 @@ class MessageActivity : AppCompatActivity() {
e.printStackTrace()
withContext(Dispatchers.Main) {
if (allMessages.isEmpty()) {
showEmptyState()
noNetwork()
}
}
} finally {
@@ -151,6 +151,12 @@ class MessageActivity : AppCompatActivity() {
binding.rvMessageList.visibility = View.VISIBLE
binding.emptyLayout.visibility = View.GONE
}
private fun noNetwork() {
binding.rvMessageList.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
binding.tvNoMoreData.visibility = View.GONE
}
private fun showNoMoreDataHint() {
binding.tvNoMoreData.visibility = View.VISIBLE
@@ -160,7 +166,7 @@ class MessageActivity : AppCompatActivity() {
binding.tvNoMoreData.visibility = View.GONE
}
private fun convertToMessageListItems(messages: List<com.example.defenseapplication.bean.MessageBean>): List<MessageListItem> {
private fun convertToMessageListItems(messages: List<com.ckkj.defense_device.bean.MessageBean>): List<MessageListItem> {
val items = mutableListOf<MessageListItem>()
val groupedMessages = messages.groupBy { extractDate(it.createTime ?: "") }
groupedMessages.forEach { (date, messageList) ->

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.content.Intent
import android.os.Bundle
@@ -9,21 +9,21 @@ import android.view.ViewGroup
import android.widget.Toast
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.UserInfoBean
import com.example.defenseapplication.databinding.PersonalFragmentBinding
import com.example.defenseapplication.liveVideo.LinkedUserActivity
import com.example.defenseapplication.personal.FaceManagementActivity
import com.example.defenseapplication.personal.PasswordManagementActivity
import com.example.defenseapplication.personal.PersonalProfileActivity
import com.example.defenseapplication.utils.AppLanguage
import com.example.defenseapplication.utils.LanguageUtil
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.utils.UserinfoUtil
import com.example.defenseapplication.utils.ActivityManager
import com.example.defenseapplication.view.FamilySwitchDialog
import com.example.defenseapplication.view.LanguageDialog
import com.example.defenseapplication.home.HomeActivity
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.UserInfoBean
import com.ckkj.defense_device.databinding.PersonalFragmentBinding
import com.ckkj.defense_device.liveVideo.LinkedUserActivity
import com.ckkj.defense_device.personal.FaceManagementActivity
import com.ckkj.defense_device.personal.PasswordManagementActivity
import com.ckkj.defense_device.personal.PersonalProfileActivity
import com.ckkj.defense_device.utils.AppLanguage
import com.ckkj.defense_device.utils.LanguageUtil
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.utils.ActivityManager
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.ckkj.defense_device.view.FamilySwitchDialog
import com.ckkj.defense_device.view.LanguageDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -49,7 +49,7 @@ class PersonalFragment : Fragment() {
.titleBar(binding.headerLayout)
.init()
Glide.with(this)
.load(R.drawable.img)
.load(R.mipmap.img)
.circleCrop()
.into(binding.ivAvatar)
binding.headerLayout.setOnClickListener {
@@ -101,27 +101,36 @@ class PersonalFragment : Fragment() {
}
// 退出登录点击事件
binding.btnLogout.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService().logout()
withContext(Dispatchers.Main) {
if (response.code == 200) {
UserinfoUtil.clearUserInfo()
val intent = Intent(requireContext(), com.example.defenseapplication.login.LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
requireActivity().finish()
} else {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
val dialog = CustomDialogFragmentText()
.setTitle(getString(R.string.tip))
.setMessage(getString(R.string.logout_confirm))
.setShowInput(false)
.setNegativeText(getString(R.string.cancel))
.setPositiveText(getString(R.string.confirm))
.setOnConfirmListener {
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService().logout()
withContext(Dispatchers.Main) {
if (response.code == 200) {
UserinfoUtil.clearUserInfo()
val intent = Intent(requireContext(), com.ckkj.defense_device.login.LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
requireActivity().finish()
} else {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
}
}
}
dialog.show(childFragmentManager, "logout_dialog")
}
fetchCurrentFamily()
return binding.root
@@ -243,8 +252,8 @@ class PersonalFragment : Fragment() {
}
userInfo.avatar?.let {
val requestOptions = RequestOptions()
.placeholder(R.drawable.img) // 加载中显示
.error(R.drawable.img) // 失败时显示
.placeholder(R.mipmap.img) // 加载中显示
.error(R.mipmap.img) // 失败时显示
Glide.with(this)
.load(it) // 如果 avatar 为 nullGlide 会直接回调 error

View File

@@ -1,14 +1,13 @@
package com.example.defenseapplication.home
package com.ckkj.defense_device.home
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.bean.AddDeviceBean
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.SetNameActivityBinding
import com.example.defenseapplication.liveVideo.LiveVideoActivity
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.bean.AddDeviceBean
import com.ckkj.defense_device.databinding.SetNameActivityBinding
import com.ckkj.defense_device.liveVideo.LiveVideoActivity
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -0,0 +1,41 @@
package com.ckkj.defense_device.home
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import com.ckkj.defense_device.databinding.ActivityStartBinding
import com.ckkj.defense_device.login.LoginActivity
import com.ckkj.defense_device.utils.ApiConfig
import com.ckkj.defense_device.utils.UserinfoUtil
import com.gyf.immersionbar.ImmersionBar
class StartActivity : AppCompatActivity() {
private lateinit var binding: ActivityStartBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityStartBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(null)
.init()
// 延迟2秒后再判断登录状态
Handler(Looper.getMainLooper()).postDelayed({
val userInfo = UserinfoUtil.getUserInfo()
if (userInfo != null) {
// 设置token
ApiConfig.authToken = userInfo.access_token
val intent = Intent(this, HomeActivity::class.java)
startActivity(intent)
} else {
val intent = Intent(this, LoginActivity::class.java)
startActivity(intent)
}
finish()
}, 2000) // 2秒延迟
}
}

View File

@@ -1,21 +1,22 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AddLinkedUserAdapter
import com.example.defenseapplication.adapter.UserItem
import com.example.defenseapplication.bean.AddDeviceUserRequest
import com.example.defenseapplication.bean.FamilyMembersBean
import com.example.defenseapplication.databinding.AddLinkedUserActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.DeviceUserUpdateEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.AddLinkedUserAdapter
import com.ckkj.defense_device.adapter.UserItem
import com.ckkj.defense_device.bean.AddDeviceUserRequest
import com.ckkj.defense_device.bean.FamilyMembersBean
import com.ckkj.defense_device.databinding.AddLinkedUserActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUserUpdateEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -95,19 +96,33 @@ class AddLinkedUserActivity : AppCompatActivity() {
Toast.makeText(this@AddLinkedUserActivity, "暂无家庭成员数据", Toast.LENGTH_SHORT).show()
}
} else {
hide()
Toast.makeText(this@AddLinkedUserActivity, "请求失败: ${response.msg}", Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
android.util.Log.e("AddLinkedUserActivity", "网络请求异常", e)
withContext(Dispatchers.Main) {
binding.swipeRefresh.isRefreshing = false
noNetwork()
Toast.makeText(this@AddLinkedUserActivity, "网络请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun hide() {
binding.searchLayout.visibility = View.GONE
binding.rvAddLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoPermission?.visibility = View.VISIBLE
}
private fun noNetwork() {
binding.searchLayout.visibility = View.GONE
binding.rvAddLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoNetwork?.visibility = View.VISIBLE
}
private fun updateFamilyMemberList(familyMembersList: List<FamilyMembersBean>) {
fullUserItemList.clear()
familyMembersList.forEach { member ->

View File

@@ -0,0 +1,429 @@
package com.ckkj.defense_device.liveVideo
import android.content.Context
import android.graphics.Color
import android.media.AudioManager
import android.media.MediaScannerConnection
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.lifecycle.lifecycleScope
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.AlarmLogAdapter
import com.ckkj.defense_device.bean.DeviceAlarmBean
import com.ckkj.defense_device.databinding.AlarmLogActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import com.bigkoo.pickerview.builder.TimePickerBuilder
import com.bigkoo.pickerview.listener.OnTimeSelectListener
import com.ckkj.defense_device.view.CustomDialogFragment
import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.MyGSYVideoPlayer
import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.launch
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
import android.net.Uri
import com.chiclaim.android.downloader.DownloadRequest
import com.chiclaim.android.downloader.DownloadListener
//告警记录
class AlarmLogActivity : AppCompatActivity() {
private lateinit var binding: AlarmLogActivityBinding
private lateinit var calendar: Calendar
private var adapter: AlarmLogAdapter? = null
private val alarmList = mutableListOf<DeviceAlarmBean>()
private val originalAlarmList = mutableListOf<DeviceAlarmBean>()
private var deviceId: String? = null
private var isLoading = false
private var videoUrl: String? = null
private var orientationUtils: OrientationUtils? = null
private var isMute = false
private lateinit var audioManager: AudioManager
private var currentAlarmItem: DeviceAlarmBean? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = AlarmLogActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(null)
.init()
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
deviceId = intent.getStringExtra("device_id")
videoUrl = intent.getStringExtra("video_url")
calendar = Calendar.getInstance()
updateDateDisplay()
initVideoPlayer()
initRecyclerView()
fetchAlarmList()
initListener()
}
private fun initVideoPlayer() {
val videoUrl = intent.getStringExtra("video_url") ?: "https://media.w3.org/2010/05/sintel/trailer.mp4"
val progressDrawable = ContextCompat.getDrawable(this, R.drawable.progress_video)
val thumbDrawable = ContextCompat.getDrawable(this, R.drawable.thumb_video)
orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
orientationUtils?.isEnable = true
GSYVideoOptionBuilder()
.setUrl(videoUrl)
.setVideoTitle("")
.setNeedLockFull(true)
.setCacheWithPlay(false)
.setRotateViewAuto(true)
.setLockLand(false)
.setShowFullAnimation(true)
.setNeedShowWifiTip(false)
.setEnlargeImageRes(R.mipmap.full_size)
.setShrinkImageRes(R.mipmap.full_screen)
.setBottomShowProgressBarDrawable(progressDrawable, thumbDrawable)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onPrepared(url: String?, vararg objects: Any?) {
super.onPrepared(url, *objects)
orientationUtils?.isEnable = true
}
override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo()
}
})
.build(binding.exoPlayContextId)
binding.exoPlayContextId.backButton.setOnClickListener {
if (orientationUtils?.isLand == 1) {
// 当前全屏,先退出全屏
binding.exoPlayContextId.fullscreenButton.performClick()
} else {
finish()
}
}
binding.exoPlayContextId.onControlVisibilityChanged = { show ->
Log.e("GSY_TEST", "Activity收到回调 show=$show")
val visible = if (show) View.VISIBLE else View.GONE
binding.ivDownload.visibility = visible
binding.ivDelete.visibility = visible
binding.ivMute.visibility = visible
}
binding.exoPlayContextId.startPlayLogic()
binding.exoPlayContextId.fullscreenButton.setOnClickListener {
orientationUtils?.resolveByClick()
binding.exoPlayContextId.startWindowFullscreen(this, true, true)?.let { fullPlayer ->
if (fullPlayer is MyGSYVideoPlayer) {
fullPlayer.onControlVisibilityChanged = { show ->
val visible = if (show) View.VISIBLE else View.GONE
binding.ivDownload.visibility = visible
binding.ivDelete.visibility = visible
binding.ivMute.visibility = visible
}
}
}
}
}
private fun initRecyclerView() {
adapter = AlarmLogAdapter(alarmList) { item ->
currentAlarmItem = item
if (!item.video.isNullOrEmpty()) {
binding.exoPlayContextId.setUp(item.video, true, item.categoryName)
binding.exoPlayContextId.startPlayLogic()
} else {
Toast.makeText(this, "该告警记录没有视频", Toast.LENGTH_SHORT).show()
}
}
binding.alarmListRecyclerView.layoutManager = LinearLayoutManager(this)
binding.alarmListRecyclerView.adapter = adapter
}
private fun fetchAlarmList() {
val currentDeviceId = deviceId ?: return
Log.d("AlarmLogActivity", "=== 开始请求设备告警日志 ===")
Log.d("AlarmLogActivity", "请求参数 - deviceId: $currentDeviceId")
isLoading = true
lifecycleScope.launch {
try {
Log.d("AlarmLogActivity", "正在发送网络请求...")
val response = RetrofitNetwork.getNetworkService().getDeviceAlarmList(
deviceId = currentDeviceId,
pageNum = "1",
pageSize = "10"
)
Log.d("AlarmLogActivity", "请求完成")
Log.d("AlarmLogActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
val newData = response.rows
originalAlarmList.clear()
originalAlarmList.addAll(newData)
filterAlarmListByDate(calendar)
currentAlarmItem = alarmList.firstOrNull()
Log.d("AlarmLogActivity", "已获取 ${newData.size} 条数据")
} catch (e: Exception) {
Log.e("AlarmLogActivity", "请求异常: ${e.message}", e)
} finally {
isLoading = false
Log.d("AlarmLogActivity", "请求结束")
}
}
}
private fun showEmptyView() {
binding.alarmListRecyclerView.visibility = View.GONE
binding.emptyLayout.visibility = View.VISIBLE
}
private fun hideEmptyView() {
binding.alarmListRecyclerView.visibility = View.VISIBLE
binding.emptyLayout.visibility = View.GONE
}
private fun filterAlarmListByDate(selectedDate: Calendar) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val selectedDateStr = dateFormat.format(selectedDate.time)
alarmList.clear()
originalAlarmList.forEach { item ->
val itemDateStr = item.createTime.substring(0, 10)
if (itemDateStr == selectedDateStr) {
alarmList.add(item)
}
}
adapter?.notifyDataSetChanged()
Log.d("AlarmLogActivity", "日期筛选完成,筛选后条数: ${alarmList.size}")
if (alarmList.isEmpty()) {
showEmptyView()
currentAlarmItem = null
} else {
hideEmptyView()
currentAlarmItem = alarmList.firstOrNull()
}
}
private fun updateDateDisplay() {
val month = calendar.get(Calendar.MONTH) + 1
val day = calendar.get(Calendar.DAY_OF_MONTH)
binding.tvDate.text = "${month}${day}"
}
private fun initListener() {
binding.expandImg.setOnClickListener {
showDatePicker()
}
binding.ivDownload.setOnClickListener {
// val item = currentAlarmItem
//
// if (item?.video.isNullOrEmpty()) {
// Toast.makeText(
// this,
// "没有可下载的视频",
// Toast.LENGTH_SHORT
// ).show()
// return@setOnClickListener
// }
showConfirmDialog(
getString(R.string.tip),
getString(R.string.download_alarm_video_confirm)
) {
//downloadVideo(item!!.video)
downloadVideo(
"https://media.w3.org/2010/05/sintel/trailer.mp4"
)
Toast.makeText(
this,
getString(R.string.download_start),
Toast.LENGTH_SHORT
).show()
}
}
binding.ivDelete.setOnClickListener {
val item = currentAlarmItem
if (item == null) {
Toast.makeText(this, "没有可删除的告警记录", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
showConfirmDialog(getString(R.string.tip), getString(R.string.delete_alarm_video_confirm)) {
val loadingDialog = LoadingDialog(this).setMessage("删除中...").apply { show() }
deleteAlarmItem(item.id, loadingDialog)
}
}
binding.ivMute.setOnClickListener {
isMute = !isMute
if (isMute) {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0)
binding.ivMute.setImageResource(
R.mipmap.mute
)
} else {
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 2, 0)
binding.ivMute.setImageResource(
R.mipmap.un_mute
)
}
}
}
private fun showDatePicker() {
val timePicker = TimePickerBuilder(this, OnTimeSelectListener { date, _ ->
calendar.time = date
updateDateDisplay()
filterAlarmListByDate(calendar)
})
.setType(booleanArrayOf(true, true, true, false, false, false)) // 年月日
.setTitleText(getString(R.string.select_date))
.setSubmitColor(Color.parseColor("#09C2BD"))//确定按钮文字颜色
.setCancelColor(Color.parseColor("#09C2BD"))//取消按钮文字颜色
.setSubCalSize(18)//确定和取消文字大小
.setTextColorCenter(Color.parseColor("#09C2BD"))//选中颜色
.setLineSpacingMultiplier(2.5f)//行间距倍数
.setItemVisibleCount(5)//可见项数量
.setDate(calendar) // 设置默认选中日期为tvDate显示的日期
.build()
timePicker.show()
}
override fun onPause() {
super.onPause()
binding.exoPlayContextId.onVideoPause()
}
override fun onResume() {
super.onResume()
binding.exoPlayContextId.onVideoResume()
}
private fun showConfirmDialog(title: String, message: String, onConfirm: () -> Unit) {
val config = CustomDialogFragment.Config(
title = title,
message = message,
positiveText = getString(R.string.confirm),
negativeText = getString(R.string.cancel)
)
showDefenseDialog(config, onPositiveClick = { onConfirm() })
}
private fun downloadVideo(url: String) {
val dir = File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES
),
"AlarmVideo"
)
if (!dir.exists()) {
dir.mkdirs()
}
val targetFile = File(
dir,
"${System.currentTimeMillis()}.mp4"
)
val request = DownloadRequest(
this,
url
)
.setDestinationUri(Uri.fromFile(targetFile))
.setNotificationTitle("${"视频名称"}:视频下载")
.registerListener(object : DownloadListener {
override fun onDownloadStart() {
Log.d("DOWNLOAD", "开始下载")
}
override fun onProgressUpdate(percent: Int) {
Log.d("DOWNLOAD", "进度:$percent%")
}
override fun onDownloadComplete(uri: Uri) {
MediaScannerConnection.scanFile(
this@AlarmLogActivity,
arrayOf(targetFile.absolutePath),
arrayOf("video/mp4"),
null
)
runOnUiThread {
ToastUtils.showSuccess(
this@AlarmLogActivity,
"下载成功"
)
}
}
override fun onDownloadFailed(e: Throwable) {
runOnUiThread {
ToastUtils.showError(
this@AlarmLogActivity,
"下载失败:${e.message}"
)
}
}
})
request.startDownload()
}
private fun deleteAlarmItem(id: String, loadingDialog: LoadingDialog) {
lifecycleScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().deleteMessage(id)
if (response.code == 200) {
Toast.makeText(this@AlarmLogActivity, getString(R.string.delete_success), Toast.LENGTH_SHORT).show()
originalAlarmList.removeAll { it.id == id }
alarmList.removeAll { it.id == id }
adapter?.notifyDataSetChanged()
currentAlarmItem = alarmList.firstOrNull()
if (alarmList.isEmpty()) {
showEmptyView()
}
} else {
Toast.makeText(this@AlarmLogActivity, response.msg, Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Toast.makeText(this@AlarmLogActivity, "删除失败: ${e.message}", Toast.LENGTH_SHORT).show()
} finally {
loadingDialog.dismiss()
}
}
}
override fun onDestroy() {
super.onDestroy()
orientationUtils?.releaseListener()
binding.exoPlayContextId.release()
}
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.content.Intent
import android.graphics.Color
@@ -8,12 +8,12 @@ import java.util.Calendar
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.AutoModeActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.DeviceUpdateEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.AutoModeNoticeDialog
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.AutoModeActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUpdateEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.AutoModeNoticeDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -173,7 +173,7 @@ class AutoModeActivity : AppCompatActivity() {
CoroutineScope(Dispatchers.IO).launch {
try {
val deviceBean = com.example.defenseapplication.bean.DeviceBean(
val deviceBean = com.ckkj.defense_device.bean.DeviceBean(
id = deviceId,
workMode = selectedMode,
startTime = startTime,

View File

@@ -1,12 +1,11 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.databinding.DeviceInfoActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.databinding.DeviceInfoActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -55,7 +54,7 @@ class DeviceInfoActivity : AppCompatActivity() {
}
}
private fun updateDeviceInfo(device: com.example.defenseapplication.bean.DeviceBean) {
private fun updateDeviceInfo(device: com.ckkj.defense_device.bean.DeviceBean) {
binding.tvDeviceModelValue.text = device.deviceNo.toString()
binding.tvDeviceNoValue.text = device.deviceSn.toString()
binding.tvFwVersionValue.text = device.fwVer.toString()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.content.Intent
import android.os.Bundle
@@ -11,22 +11,21 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.LinkedUser
import com.example.defenseapplication.adapter.LinkedUserAdapter
import com.example.defenseapplication.bean.DeleteDeviceUserRequest
import com.example.defenseapplication.bean.DeviceUserBean
import com.example.defenseapplication.bean.FamilyMembersBean
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.databinding.LinkedUserActivityBinding
import com.example.defenseapplication.personal.PasswordInputDialog
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.DeviceUserUpdateEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.LinkedUser
import com.ckkj.defense_device.adapter.LinkedUserAdapter
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
import com.ckkj.defense_device.bean.DeviceUserBean
import com.ckkj.defense_device.bean.FamilyMembersBean
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.databinding.LinkedUserActivityBinding
import com.ckkj.defense_device.personal.PasswordInputDialog
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUserUpdateEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.gyf.immersionbar.ImmersionBar
import com.yanzhenjie.recyclerview.SwipeMenuItem
import com.yanzhenjie.recyclerview.SwipeRecyclerView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -258,6 +257,7 @@ class LinkedUserActivity : AppCompatActivity() {
updateFamilyMembersList(response.data as List<FamilyMembersBean>)
}
} else {
hide()
Toast.makeText(this@LinkedUserActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
@@ -265,11 +265,24 @@ class LinkedUserActivity : AppCompatActivity() {
e.printStackTrace()
withContext(Dispatchers.Main) {
binding.swipeRefresh.isRefreshing = false
Toast.makeText(this@LinkedUserActivity, "网络请求失败", Toast.LENGTH_SHORT).show()
noNetwork()
Toast.makeText(this@LinkedUserActivity, R.string.network_request_failed, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun hide() {
binding.searchLayout.visibility = View.GONE
binding.rvLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoPermission?.visibility = View.VISIBLE
}
private fun noNetwork() {
binding.searchLayout.visibility = View.GONE
binding.rvLinkedUser.visibility = View.GONE
binding.butting?.visibility = View.GONE
binding.layoutNoNetwork?.visibility = View.VISIBLE
}
private fun updateDeviceUserList(deviceUserList: List<DeviceUserBean>) {
fullUserList.clear()
@@ -339,7 +352,7 @@ class LinkedUserActivity : AppCompatActivity() {
binding.fabAdd.setOnClickListener {
if (checkUserLimit()) {
val intent = Intent(this, com.example.defenseapplication.personal.InvitationCodeActivity::class.java)
val intent = Intent(this, com.ckkj.defense_device.personal.InvitationCodeActivity::class.java)
startActivity(intent)
}
}

View File

@@ -0,0 +1,290 @@
package com.ckkj.defense_device.liveVideo
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.AlarmLogHorizontalAdapter
import com.ckkj.defense_device.base.BaseActivity
import com.ckkj.defense_device.bean.DeviceAlarmBean
import com.ckkj.defense_device.databinding.LiveVideoActivityBinding
import com.ckkj.defense_device.utils.ActivityManager
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope
class LiveVideoActivity : BaseActivity() {
private lateinit var binding: LiveVideoActivityBinding
private var isVoiceEnabled = false
private var isLightOn = false
private var isConsoleVisible = false
private var deviceId: String? = null
private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf()
private var alarmAdapter: AlarmLogHorizontalAdapter? = null
private var currentPage = 1
private var totalPage = 1
private val pageSize = 10
private var hasMore = true
private var isLoading = false
private var orientationUtils: OrientationUtils? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("onEnterFullscreen", "onCreate")
binding = LiveVideoActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
deviceId = intent.getStringExtra("device_id")
val deviceName = intent.getStringExtra("device_name")
if (!deviceName.isNullOrEmpty()) {
binding.tvTitle.text = deviceName
}
initVideoPlayer()
initAlarmRecyclerView()
initListener()
fetchAlarmList()
}
/*
* 这段代码重写了onConfigurationChanged方法用于监听屏幕方向变化
当屏幕旋转为横屏时隐藏顶部栏topBar
当屏幕为竖屏时,显示顶部栏
目的是在横屏观看视频时提供更沉浸的全屏体验。
* */
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
binding.topBar.visibility = View.GONE
} else {
binding.topBar.visibility = View.VISIBLE
}
}
private fun initVideoPlayer() {
val videoUrl ="http://220.203.23.195:9090/live/test001.live.flv"
orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
orientationUtils?.isEnable = true
GSYVideoOptionBuilder()
.setUrl(videoUrl)
.setCacheWithPlay(false)
.setCacheWithPlay(false)
.setRotateViewAuto(true)
.setLockLand(false)
.setShowFullAnimation(true)
.setNeedShowWifiTip(false)
.setEnlargeImageRes(R.drawable.full_size)
.setShrinkImageRes(R.drawable.full_screen)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onPrepared(url: String?, vararg objects: Any?) {
super.onPrepared(url, *objects)
orientationUtils?.isEnable = true
}
override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo()
}
})
.build(binding.exoPlayContextId)
hidePlayerControls()
binding.exoPlayContextId.startPlayLogic()
binding.exoPlayContextId.fullscreenButton.setOnClickListener {
orientationUtils?.resolveByClick()
binding.exoPlayContextId.startWindowFullscreen(this, true, true)
}
}
private fun hidePlayerControls() {
binding.exoPlayContextId.backButton.visibility = View.GONE
binding.exoPlayContextId.titleTextView.visibility = View.GONE
binding.exoPlayContextId.startButton.visibility = View.GONE
binding.exoPlayContextId.findViewById<View>(
com.shuyu.gsyvideoplayer.R.id.current
)?.visibility = View.GONE
binding.exoPlayContextId.findViewById<View>(
com.shuyu.gsyvideoplayer.R.id.total
)?.visibility = View.GONE
binding.exoPlayContextId.findViewById<View>(
com.shuyu.gsyvideoplayer.R.id.progress
)?.visibility = View.INVISIBLE
binding.exoPlayContextId.findViewById<View>(
com.shuyu.gsyvideoplayer.R.id.start
)?.visibility = View.GONE
binding.exoPlayContextId.findViewById<View>(
com.shuyu.gsyvideoplayer.R.id.layout_bottom
)?.setBackgroundColor(Color.TRANSPARENT)
}
private fun initAlarmRecyclerView() {
val layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
binding.alarmLogRecyclerView.layoutManager = layoutManager
alarmAdapter = AlarmLogHorizontalAdapter(alarmList) { messageBean ->
val intent = Intent(this, AlarmLogActivity::class.java)
intent.putExtra("device_id", deviceId)
intent.putExtra("selected_id", messageBean.id)
intent.putExtra("video_url", messageBean.video)
intent.putExtra("create_time", messageBean.createTime)
intent.putExtra("from_live_video", true)
startActivity(intent)
}
binding.alarmLogRecyclerView.adapter = alarmAdapter
binding.alarmLogRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dx > 0 && !isLoading && hasMore) {
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
if (lastVisibleItemPosition == alarmList.size - 1) {
loadMoreAlarms()
}
}
}
})
}
private fun loadMoreAlarms() {
if (!hasMore || isLoading) return
currentPage++
if (currentPage > totalPage) {
hasMore = false
return
}
fetchAlarmList(currentPage)
}
private fun initListener() {
binding.btnBack.setOnClickListener {
ActivityManager.finishExceptHome()
val intent = Intent(this, com.ckkj.defense_device.home.HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
startActivity(intent)
finish()
}
binding.ivMenu.setOnClickListener {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
binding.llLight.setOnClickListener {
isLightOn = !isLightOn
if (isLightOn) {
binding.ivLight.setImageResource(com.ckkj.defense_device.R.mipmap.light_on_open)
} else {
binding.ivLight.setImageResource(com.ckkj.defense_device.R.mipmap.light_on)
}
}
binding.xq.setOnClickListener {
val intent = Intent(this, AlarmLogActivity::class.java)
intent.putExtra("device_id", deviceId)
intent.putExtra("from_live_video", true)
startActivity(intent)
}
binding.llRecord.setOnClickListener {
isConsoleVisible = !isConsoleVisible
if (isConsoleVisible) {
binding.llAlarmLog.visibility = View.GONE
binding.consoleBar.visibility = View.VISIBLE
binding.ivRecord.setImageResource(R.mipmap.copy_open)
} else {
binding.llAlarmLog.visibility = View.VISIBLE
binding.consoleBar.visibility = View.GONE
binding.ivRecord.setImageResource(R.mipmap.copy)
}
}
binding.llVoice.setOnClickListener {
isVoiceEnabled = !isVoiceEnabled
if (isVoiceEnabled) {
Glide.with(this)
.asGif()
.load(R.mipmap.voice_open)
.into(binding.ivVoice)
} else {
binding.ivVoice.setImageResource(R.mipmap.voice)
}
}
}
private fun fetchAlarmList(page: Int = 1) {
val currentDeviceId = deviceId ?: return
isLoading = true
lifecycleScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().getDeviceAlarmList(
deviceId = currentDeviceId,
pageNum = page.toString(),
pageSize = pageSize.toString()
)
Log.d("LiveVideoActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
val newData = response.rows
totalPage = if (response.total % pageSize == 0) {
response.total / pageSize
} else {
response.total / pageSize + 1
}
if (page == 1) {
alarmList.clear()
hasMore = totalPage > 1
}
alarmList.addAll(newData)
alarmAdapter?.notifyDataSetChanged()
} catch (e: Exception) {
hasMore = false
} finally {
isLoading = false
}
}
}
override fun onResume() {
super.onResume()
binding.exoPlayContextId.onVideoResume()
}
override fun onPause() {
super.onPause()
binding.exoPlayContextId.onVideoPause()
}
override fun onDestroy() {
super.onDestroy()
orientationUtils?.releaseListener()
binding.exoPlayContextId.release()
}
}

View File

@@ -1,19 +1,19 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import com.example.defenseapplication.R
import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.databinding.SettingsActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.example.defenseapplication.view.RecognitionModeDialog
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.personal.PasswordInputDialog
import com.ckkj.defense_device.R
import com.ckkj.defense_device.base.BaseActivity
import com.ckkj.defense_device.databinding.SettingsActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.ckkj.defense_device.view.RecognitionModeDialog
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.personal.PasswordInputDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -183,7 +183,7 @@ class SettingsActivity : BaseActivity() {
binding.tvRelatedUsers.text = "$count"
}
private fun updateDeviceInfo(device: com.example.defenseapplication.bean.DeviceBean) {
private fun updateDeviceInfo(device: com.ckkj.defense_device.bean.DeviceBean) {
binding.tvDeviceName.text = device.deviceName ?: "未设置"
binding.tvWifiName.text = device.wifiName ?: "未设置"
@@ -236,7 +236,7 @@ class SettingsActivity : BaseActivity() {
CoroutineScope(Dispatchers.IO).launch {
try {
val deviceBean = com.example.defenseapplication.bean.DeviceBean(
val deviceBean = com.ckkj.defense_device.bean.DeviceBean(
id = deviceId,
deviceName = name,
)
@@ -273,7 +273,7 @@ class SettingsActivity : BaseActivity() {
CoroutineScope(Dispatchers.IO).launch {
try {
val deviceBean = com.example.defenseapplication.bean.DeviceBean(
val deviceBean = com.ckkj.defense_device.bean.DeviceBean(
id = deviceId,
patternPerception = patternPerception,
)
@@ -305,13 +305,13 @@ class SettingsActivity : BaseActivity() {
CoroutineScope(Dispatchers.IO).launch {
try {
val deleteBean = com.example.defenseapplication.bean.DeleteBean()
val deleteBean = com.ckkj.defense_device.bean.DeleteBean()
deleteBean.deviceId = deviceId
val response = RetrofitNetwork.getNetworkService().deleteDevice(deleteBean)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@SettingsActivity, "解绑成功", Toast.LENGTH_SHORT).show()
val intent = Intent(this@SettingsActivity, com.example.defenseapplication.home.HomeActivity::class.java)
val intent = Intent(this@SettingsActivity, com.ckkj.defense_device.home.HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
finish()

View File

@@ -1,17 +1,17 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.animation.ObjectAnimator
import android.os.Bundle
import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.StrikePositionActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.CustomDialogFragment
import com.example.defenseapplication.view.showDefenseDialog
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.StrikePositionActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.CustomDialogFragment
import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -45,7 +45,7 @@ class StrikePositionActivity : AppCompatActivity() {
private var selectedZone: Zone = Zone.CHEST
// Touch tracking for ImageView (captured in onTouch, used in ACTION_UP for click detection)
// ImageView 的触控追踪(在 onTouch 中捕获ACTION_UP 用于点击检测)
private var lastTouchX = 0f
private var lastTouchY = 0f
@@ -67,7 +67,7 @@ class StrikePositionActivity : AppCompatActivity() {
binding.ivBack.setOnClickListener { finish() }
binding.btnSubmit.setOnClickListener { showConfirmDialog() }
// Use OnTouchListener to track touch coordinates
// 使用 OnTouchListen 来跟踪触摸坐标
binding.ivHumanBody.setOnTouchListener { _, event ->
lastTouchX = event.x
lastTouchY = event.y
@@ -77,7 +77,7 @@ class StrikePositionActivity : AppCompatActivity() {
false
}
// OnClick fires on ACTION_UP; coordinates captured from lastTouchX/Y
// OnClick 对ACTION_UP开火;坐标来自lastTouchXY
binding.ivHumanBody.setOnClickListener {
val imageView = it as ImageView
val (nx, ny) = resolveTapLocation(lastTouchX, lastTouchY, imageView)
@@ -100,10 +100,12 @@ class StrikePositionActivity : AppCompatActivity() {
}
/**
* Converts a screen coordinate (from onTouch) into a normalised [0..1] offset
* inside the drawable, accounting for:
* - ImageView scaling (scaleType="fitCenter" adds letterboxing)
* - Letterbox / pillarbox margins when image aspect view aspect
* 这段代码将触摸坐标转换为图片内部的归一化位置
*
* 1. 计算图片等比缩放后的实际尺寸
* 2. 计算letterbox/pillarbox的偏移量
* 3. 将触摸点限制在图片有效区域内
* 4. 返回相对于图片的归一化坐标(0-1范围)
*/
private fun resolveTapLocation(rawX: Float, rawY: Float, imageView: ImageView): Pair<Float, Float> {
val drawable = imageView.drawable ?: return Pair(0f, 0f)
@@ -114,22 +116,22 @@ class StrikePositionActivity : AppCompatActivity() {
val intrinsicWidth = drawable.intrinsicWidth.toFloat()
val intrinsicHeight = drawable.intrinsicHeight.toFloat()
// scale: the uniform factor the image is scaled by to fit the view
// 比例:图像被缩放以适应视图的均匀因子
val scale = min(ivWidth / intrinsicWidth, ivHeight / intrinsicHeight)
// Physical pixel size of the drawable after scaling
// 缩放后绘制的物理像素大小
val drawnW = intrinsicWidth * scale
val drawnH = intrinsicHeight * scale
// Letterbox offsets (top / left) when aspect ratios differ
// 当宽高比不同时,信箱偏移(左上角)
val drawableLeft = (ivWidth - drawnW) / 2f
val drawableTop = (ivHeight - drawnH) / 2f
// Clamp touch to the actual drawable area
// 夹紧触地接触实际可绘制区域
val clampedX = max(drawableLeft, min(rawX, drawableLeft + drawnW))
val clampedY = max(drawableTop, min(rawY, drawableTop + drawnH))
// Normalised offset [0..1] within the drawable
// 可绘制物内的归一化偏移 [0..1]
val normalizedX = (clampedX - drawableLeft) / drawnW
val normalizedY = (clampedY - drawableTop) / drawnH
@@ -137,14 +139,11 @@ class StrikePositionActivity : AppCompatActivity() {
}
/**
* Zones are divided by:
* - Horizontal boundary at ny = 0.5 top = HEAD, bottom = shoulders
* - Vertical boundary 1 at nx = 1/3 left = LEFT_SHOULDER
* - Vertical boundary 2 at nx = 2/3 right = RIGHT_SHOULDER, middle = CHEST
* 这段代码根据归一化坐标确定触摸区域
*
* If the touch falls within ±tolerance of any boundary line, the nearest
* zone centre (Euclidean distance in normalised space) is used to avoid
* arbitrary assignments at boundary pixels.
* 1. 将图片分为头部左肩胸部右肩四个区域
* 2. 边界附近±0.04范围内使用最近区域中心避免误判
* 3. 上半部分为头部下半部分按1/3和2/3垂直线划分
*/
private fun zoneForLocation(nx: Float, ny: Float): Zone {
val hBoundary = 0.5f
@@ -168,7 +167,14 @@ class StrikePositionActivity : AppCompatActivity() {
}
}
/** Returns the Zone whose normalised centre is closest (Euclidean) to (nx, ny). */
/**
* 这段代码找到距离触摸点最近的区域
*
* 1. 遍历所有Zone计算与触摸点的欧氏距离平方
* 2. 返回距离最小的Zone作为结果
* 3. 若无匹配则默认返回CHEST
* */
private fun nearestZone(nx: Float, ny: Float): Zone {
return Zone.values().minByOrNull { zone ->
val dx = nx - zone.normalizedX
@@ -178,12 +184,12 @@ class StrikePositionActivity : AppCompatActivity() {
}
/**
* Positions the marker (ivStrikeMarker) at the zone's centre, clamped so the
* marker never goes out of the ivHumanBody bounds.
*
* Because the marker is a child of ivHumanBody and starts at its center
* (layout_gravity="center"), we compute the offset from that center point
* and apply it via translationX/Y so the marker's layout bounds stay intact.
这段代码更新标记位置到区域中心
1. 根据Zone的归一化坐标计算目标位置
2. 限制标记不超出图片边界Y仅限制上限
3. 相对于ImageView中心计算偏移量并应用
4. 支持动画或即时移动两种方式
*/
private fun updateMarkerPosition(zone: Zone, animated: Boolean) {
if (humanBodyWidth <= 0 || humanBodyHeight <= 0) return
@@ -198,9 +204,7 @@ class StrikePositionActivity : AppCompatActivity() {
val rawX = humanBodyWidth * zone.normalizedX
val rawY = humanBodyHeight * zone.normalizedY
// Clamp X so the marker never extends beyond the drawable edges.
// Y clamp only has an upper bound — HEAD at normalizedY=0 is allowed to go
// all the way to the top (translationY can be negative).
//用X夹住使标记永远不会超出可绘制的边缘。Y钳只有一个上界——在归一化Y=0处的HEAD可以一直达到顶端平移Y可以为负
val clampedX = min(max(rawX, halfMarkerW), humanBodyWidth - halfMarkerW)
val clampedY = min(rawY, humanBodyHeight - halfMarkerH)

View File

@@ -1,20 +1,20 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.DeleteDeviceUserRequest
import com.example.defenseapplication.bean.ObtainInviterInformationRequest
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.personal.PasswordInputDialog
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.DeviceUserUpdateEvent
import com.example.defenseapplication.databinding.UserDetailsActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.personal.PasswordInputDialog
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.DeviceUserUpdateEvent
import com.ckkj.defense_device.databinding.UserDetailsActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -27,6 +27,7 @@ class UserDetailsActivity : AppCompatActivity() {
private var userId: String = ""
private var isPasswordVerified: Boolean = false
private lateinit var passwordDialog: PasswordInputDialog
private var optionPassword: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -73,7 +74,7 @@ class UserDetailsActivity : AppCompatActivity() {
}
}
private fun updateUI(data: com.example.defenseapplication.bean.ObtainInviterInformationBean) {
private fun updateUI(data: com.ckkj.defense_device.bean.ObtainInviterInformationBean) {
binding.tvContactName.text = data.nickName ?: "未知用户"
binding.tvPhone.text = data.phonenumber ?: ""
binding.tvIdCard.text = data.idCard ?: ""
@@ -88,10 +89,12 @@ class UserDetailsActivity : AppCompatActivity() {
binding.swUserStatus.setOnCheckedChangeListener { _, isChecked ->
if (!isPasswordVerified) {
binding.swUserStatus.isChecked = !isChecked
showPasswordDialog {
showPasswordDialog(isChecked) {
isPasswordVerified = true
binding.swUserStatus.isChecked = isChecked
}
} else {
updateFamilyUserStatus(isChecked)
}
}
@@ -100,7 +103,7 @@ class UserDetailsActivity : AppCompatActivity() {
}
}
private fun showPasswordDialog(onVerified: () -> Unit) {
private fun showPasswordDialog(status: Boolean, onVerified: () -> Unit) {
passwordDialog = PasswordInputDialog(this).apply {
setOnConfirmListener { inputPassword ->
lifecycleScope.launch {
@@ -110,6 +113,7 @@ class UserDetailsActivity : AppCompatActivity() {
withContext(Dispatchers.Main) {
if (response.code == 200) {
dismiss()
updateFamilyUserStatus(status, inputPassword)
onVerified()
} else {
Toast.makeText(
@@ -136,6 +140,48 @@ class UserDetailsActivity : AppCompatActivity() {
}
private fun updateFamilyUserStatus(status: Boolean) {
updateFamilyUserStatus(status, optionPassword)
}
private fun updateFamilyUserStatus(status: Boolean, password: String) {
if (userId.isEmpty()) {
Toast.makeText(this, "用户ID为空", Toast.LENGTH_SHORT).show()
return
}
if (password.isNotEmpty()) {
optionPassword = password
}
lifecycleScope.launch {
try {
val request = UpdateFamilyUserRequest(
id = userId,
optionPassword = optionPassword,
optionStatus = "status",
status = if (status) "1" else "0"
)
val response = RetrofitNetwork.getNetworkService().updateFamilyUser(request)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@UserDetailsActivity, "状态修改成功", Toast.LENGTH_SHORT).show()
DeviceUserUpdateEvent.notifyUpdate()
} else {
Toast.makeText(this@UserDetailsActivity, response.msg ?: "状态修改失败", Toast.LENGTH_SHORT).show()
binding.swUserStatus.isChecked = !status
}
}
} catch (e: Exception) {
e.printStackTrace()
withContext(Dispatchers.Main) {
Toast.makeText(this@UserDetailsActivity, "网络请求失败", Toast.LENGTH_SHORT).show()
binding.swUserStatus.isChecked = !status
}
}
}
}
private fun showDeleteDialog() {
val dialog = CustomDialogFragmentText()
dialog.setTitle(getString(R.string.tip))
@@ -147,7 +193,7 @@ class UserDetailsActivity : AppCompatActivity() {
if (isPasswordVerified) {
deleteDeviceUser()
} else {
showPasswordDialog {
showPasswordDialog(false) {
isPasswordVerified = true
deleteDeviceUser()
}

View File

@@ -1,15 +1,15 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.VoiceSettingsActivityBinding
import com.example.defenseapplication.utils.ApiService
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.VoiceSettingsActivityBinding
import com.ckkj.defense_device.utils.ApiService
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -79,29 +79,31 @@ class VoiceSettingsActivity : AppCompatActivity() {
private fun updateDeviceAlertAudio() {
deviceId?.let { id ->
val newAlertAudio = if (binding.swWarningVoice.isChecked) "1" else "0"
// 只有状态发生变化时才请求
if (newAlertAudio != originalAlertAudio) {
CoroutineScope(Dispatchers.IO).launch {
try {
val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio)
val response = apiService.updateDeviceInfo(deviceBean)
withContext(Dispatchers.Main) {
if (response.code == 200 && response.data != null) {
Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show()
} else {
// 点击返回时总是触发网络请求
CoroutineScope(Dispatchers.IO).launch {
try {
val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio)
val response = apiService.updateDeviceInfo(deviceBean)
withContext(Dispatchers.Main) {
if (response.code == 200 && response.data != null) {
Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show()
} else {
// 仅在状态实际发生变化时才显示错误提示
if (newAlertAudio != originalAlertAudio) {
Toast.makeText(this@VoiceSettingsActivity, response.msg, Toast.LENGTH_SHORT).show()
}
finish()
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
finish()
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
// 仅在状态实际发生变化时才显示失败提示
if (newAlertAudio != originalAlertAudio) {
Toast.makeText(this@VoiceSettingsActivity, "修改失败", Toast.LENGTH_SHORT).show()
finish()
}
finish()
}
}
} else {
finish()
}
}
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.content.Intent
import android.graphics.Color
@@ -6,10 +6,10 @@ import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.widget.doAfterTextChanged
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.WarningVoiceContentActivityBinding
import com.example.defenseapplication.utils.ApiService
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.WarningVoiceContentActivityBinding
import com.ckkj.defense_device.utils.ApiService
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,16 +1,15 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import com.example.defenseapplication.R
import com.example.defenseapplication.base.BaseActivity
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.WaterGunSettingsActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.ContainerSelectDialog
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.ckkj.defense_device.R
import com.ckkj.defense_device.base.BaseActivity
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.WaterGunSettingsActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.ContainerSelectDialog
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.liveVideo
package com.ckkj.defense_device.liveVideo
import android.Manifest
import android.content.BroadcastReceiver
@@ -18,13 +18,13 @@ import android.widget.PopupWindow
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.WifiListAdapter
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.databinding.DialogWifiListBinding
import com.example.defenseapplication.databinding.WifiManagementActivityBinding
import com.example.defenseapplication.utils.DeviceIdEvent
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.adapter.WifiListAdapter
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.databinding.DialogWifiListBinding
import com.ckkj.defense_device.databinding.WifiManagementActivityBinding
import com.ckkj.defense_device.utils.DeviceIdEvent
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,14 +1,9 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.FaceLoginActivityBinding
import com.ckkj.defense_device.databinding.FaceLoginActivityBinding
import com.gyf.immersionbar.ImmersionBar
//人脸识别
class FaceLoginActivity : AppCompatActivity() {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.Manifest
import android.content.pm.PackageManager
@@ -13,8 +13,8 @@ import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.defenseapplication.databinding.FaceVerificationActivityBinding
import com.example.defenseapplication.view.FaceAnalyzer
import com.ckkj.defense_device.databinding.FaceVerificationActivityBinding
import com.ckkj.defense_device.view.FaceAnalyzer
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import com.gyf.immersionbar.ImmersionBar

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
@@ -7,9 +7,10 @@ import android.os.CountDownTimer
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import bean.ForgotRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ForgetPasswordActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ForgetPasswordActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
@@ -20,6 +21,7 @@ import java.util.regex.Pattern
class ForgetPasswordActivity : AppCompatActivity() {
private lateinit var binding: ForgetPasswordActivityBinding
private var isLoading = false
private var countDownTimer: CountDownTimer? = null
private var isSendingCode = false
@@ -63,7 +65,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
}
binding.btnNext.setOnClickListener {
performResetPassword()
if (!isLoading) {
performResetPassword()
}
}
}
@@ -163,8 +167,11 @@ class ForgetPasswordActivity : AppCompatActivity() {
return
}
isLoading = true
binding.btnNext.isEnabled = false
binding.btnNext.text = "重置中..."
val loadingDialog = LoadingDialog(this)
.setMessage(getString(R.string.resetting))
.apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
@@ -175,8 +182,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
)
val response = RetrofitNetwork.getNetworkService().forgotPassword(forgotRequest)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
if (response.code == 200) {
Toast.makeText(this@ForgetPasswordActivity, R.string.reset_success, Toast.LENGTH_SHORT).show()
@@ -187,8 +195,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true
binding.btnNext.text = "确定"
Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}

View File

@@ -1,21 +1,22 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.os.CountDownTimer
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import bean.LoginRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.ActivityLoginBinding
import com.example.defenseapplication.home.HomeActivity
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.utils.UserinfoUtil
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.ActivityLoginBinding
import com.ckkj.defense_device.home.HomeActivity
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -32,14 +33,6 @@ class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (UserinfoUtil.getUserInfo() != null) {
val intent = Intent(this, HomeActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
return
}
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -202,6 +195,7 @@ class LoginActivity : AppCompatActivity() {
try {
val response = RetrofitNetwork.getNetworkService().getCode(phone)
withContext(Dispatchers.Main) {
Log.d("VerificationCode", "code = ${response.code}, msg = ${response.msg}")
if (response.code == 200) {
Toast.makeText(this@LoginActivity, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show()
startCountDown()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.content.Intent
import android.graphics.Color
@@ -8,9 +8,10 @@ import android.os.CountDownTimer
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import bean.RegisterRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RegisterActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.RegisterActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -158,7 +159,9 @@ class RegisterActivity : AppCompatActivity() {
isLoading = true
binding.btnNext.isEnabled = false
binding.btnNext.text = getString(R.string.registering)
val loadingDialog = LoadingDialog(this)
.setMessage(getString(R.string.registering))
.apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
@@ -174,8 +177,8 @@ class RegisterActivity : AppCompatActivity() {
val response = RetrofitNetwork.getNetworkService().register(registerRequest)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
if (response.code == 200) {
Toast.makeText(this@RegisterActivity, R.string.register_success, Toast.LENGTH_SHORT).show()
@@ -190,8 +193,8 @@ class RegisterActivity : AppCompatActivity() {
} catch (e: Exception) {
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
binding.btnNext.isEnabled = true
binding.btnNext.text = getString(R.string.next_step)
Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}

View File

@@ -1,15 +1,11 @@
package com.example.defenseapplication.login
package com.ckkj.defense_device.login
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RegisterSuccessActivityBinding
import com.example.defenseapplication.home.HomeActivity
import com.ckkj.defense_device.databinding.RegisterSuccessActivityBinding
import com.ckkj.defense_device.home.HomeActivity
import com.gyf.immersionbar.ImmersionBar
//注册成功界面
class RegisterSuccessActivity : AppCompatActivity() {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.model
package com.ckkj.defense_device.model
data class FamilyItem(
val id: String,

View File

@@ -1,12 +1,12 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.UpdatePassword
import com.example.defenseapplication.databinding.ChangePasswordActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.UpdatePassword
import com.ckkj.defense_device.databinding.ChangePasswordActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,10 +1,10 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.databinding.FaceManagementActivityBinding
import com.example.defenseapplication.login.FaceLoginActivity
import com.ckkj.defense_device.databinding.FaceManagementActivityBinding
import com.ckkj.defense_device.login.FaceLoginActivity
import com.gyf.immersionbar.ImmersionBar
//人脸管理页面

View File

@@ -1,32 +1,45 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.bottomsheet.BottomSheetDialog
import androidx.core.content.ContextCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.databinding.DialogExpireTimeBinding
import com.example.defenseapplication.databinding.InvitationCodeActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.RecommendationCodeBean
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.databinding.DialogExpireTimeBinding
import com.ckkj.defense_device.databinding.InvitationCodeActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.google.gson.Gson
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.util.Hashtable
@@ -44,7 +57,24 @@ class InvitationCodeActivity : AppCompatActivity() {
private var isLoading = false // 是否正在加载
private var hasGeneratedQrCode = false // 是否已经生成过二维码
private val requestPermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
} else {
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
}
if (cameraGranted && storageGranted) {
startScan()
} else {
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
}
companion object {
private const val TAG = "InvitationCodeActivity"
const val EXPIRE_24H = 24 * 60 * 60 * 1000L
const val EXPIRE_7D = 7 * 24 * 60 * 60 * 1000L
}
@@ -90,15 +120,136 @@ class InvitationCodeActivity : AppCompatActivity() {
// 撤销邀请按钮
binding.btnCancelInvite.setOnClickListener {
showPasswordDialog {
cancelInvitation()
if (generatedInviteCode.isEmpty()) {
ToastUtils.showError(this, getString(R.string.qr_code_expired))
return@setOnClickListener
}
showPasswordDialog { password, dialog ->
cancelInvitation(password, dialog)
}
}
// 生成/重新生成按钮
binding.btnRegenerate.setOnClickListener {
showPasswordDialog {
generateOrRegenerateInvitation()
showPasswordDialog { password, dialog ->
generateOrRegenerateInvitation(password, dialog)
}
}
binding.sys.setOnClickListener {
checkPermissionsAndScan()
}
}
private fun checkPermissionsAndScan() {
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
} else {
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
storagePermission == PackageManager.PERMISSION_GRANTED
) {
startScan()
} else {
val permissionsToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_MEDIA_IMAGES
)
} else {
arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
requestPermissionsLauncher.launch(permissionsToRequest)
}
}
private fun startScan() {
MNScanManager.startScan(this, object : MNScanCallback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
when (resultCode) {
MNScanManager.RESULT_SUCCESS -> {
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
if (!result.isNullOrEmpty()) {
val inviteCode = parseInviteCode(result[0])
if (inviteCode.isNullOrEmpty()) {
Toast.makeText(this@InvitationCodeActivity, "无效的邀请码", Toast.LENGTH_SHORT).show()
} else {
fetchInviterInfo(inviteCode)
}
} else {
Toast.makeText(this@InvitationCodeActivity, "扫码结果为空", Toast.LENGTH_SHORT).show()
}
}
MNScanManager.RESULT_FAIL -> {
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
Toast.makeText(this@InvitationCodeActivity, "扫码失败: $error", Toast.LENGTH_SHORT).show()
}
MNScanManager.RESULT_CANCLE -> {
Toast.makeText(this@InvitationCodeActivity, "扫码取消", Toast.LENGTH_SHORT).show()
}
}
}
})
}
private fun parseInviteCode(scanResult: String): String? {
val trimmed = scanResult.trim()
if (trimmed.isEmpty()) return null
return try {
if (trimmed.startsWith("{")) {
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
} else {
trimmed
}
} catch (e: Exception) {
trimmed
}
}
private fun fetchInviterInfo(inviteCode: String) {
val loadingDialog = LoadingDialog(this).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
if (response.code == 200 && response.data != null) {
val inviter = response.data
val parentId = inviter.resolveParentId()
if (parentId.isNullOrEmpty()) {
Toast.makeText(
this@InvitationCodeActivity,
getString(R.string.network_error),
Toast.LENGTH_SHORT
).show()
return@withContext
}
val intent = Intent(this@InvitationCodeActivity, JoinFamilyInviteActivity::class.java).apply {
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
}
startActivity(intent)
} else {
Toast.makeText(
this@InvitationCodeActivity,
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
loadingDialog.dismiss()
Toast.makeText(this@InvitationCodeActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
@@ -106,14 +257,56 @@ class InvitationCodeActivity : AppCompatActivity() {
/**
* 生成或重新生成邀请码
*/
private fun generateOrRegenerateInvitation() {
// 根据当前选择的时效生成邀请码
generateQRCode()
showQrCode()
// 更新按钮文字为"重新生成"
binding.btnRegenerate.text = getString(R.string.regenerate)
hasGeneratedQrCode = true
ToastUtils.showSuccess(this, getString(R.string.operation_success))
private fun generateOrRegenerateInvitation(password: String, passwordDialog: PasswordInputDialog) {
if (isLoading) return
isLoading = true
val loadingDialog = LoadingDialog(this).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.generateRecommendationCode(VerifyOptionPasswordRequest(password))
Log.d(TAG, "生成推荐码响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) {
val parsed = parseRecommendationCode(response.data)
if (parsed != null) {
passwordDialog.dismiss()
generatedInviteCode = parsed.first
expireTimestamp = parsed.second
qrCodeContent =
"{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}"
Log.d(
TAG,
"推荐码生成成功: Code=$generatedInviteCode, expireTimestamp=$expireTimestamp, qrContent=$qrCodeContent"
)
generateQRCode()
showQrCode()
binding.btnRegenerate.text = getString(R.string.regenerate)
hasGeneratedQrCode = true
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
} else {
Log.w(TAG, "推荐码解析失败, raw data=${response.data}")
passwordDialog.showError(getString(R.string.network_error))
}
} else {
Log.w(TAG, "生成推荐码失败: code=${response.code}, msg=${response.msg}")
passwordDialog.showError(
response.msg.ifEmpty { getString(R.string.password_error) }
)
}
}
} catch (e: Exception) {
Log.e(TAG, "生成推荐码异常", e)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
passwordDialog.showError(getString(R.string.network_error))
}
}
}
}
private fun showExpireTimeDialog() {
@@ -151,9 +344,6 @@ class InvitationCodeActivity : AppCompatActivity() {
binding.tvExpireTime.text = getString(R.string.expire_7d)
}
}
// 显示二维码并生成
showQrCode()
generateQRCode()
expireTimeDialog?.dismiss()
}
@@ -173,22 +363,28 @@ class InvitationCodeActivity : AppCompatActivity() {
}
private fun generateQRCode() {
// 检查二维码是否过期,如果过期则生成新的邀请码
val isExpired = expireTimestamp > 0 && System.currentTimeMillis() > expireTimestamp
val content = if (qrCodeContent.isNotEmpty() && !isExpired) qrCodeContent else generateInviteCode()
val qrBitmap = createQRCodeWithLogo(content, 400, 400)
if (qrCodeContent.isEmpty()) return
val qrBitmap = createQRCodeWithLogo(qrCodeContent, 400, 400)
binding.ivQrCode.setImageBitmap(qrBitmap)
}
private fun generateInviteCode(): String {
val timestamp = System.currentTimeMillis()
expireTimestamp = timestamp + expireTimeInMillis
generatedInviteCode = "INV${timestamp}"
qrCodeContent = "{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}"
// 输出日志,方便调试验证
android.util.Log.d("InvitationCode", "生成的二维码内容: $qrCodeContent")
android.util.Log.d("InvitationCode", "推荐码: $generatedInviteCode, 过期时间戳: $expireTimestamp")
return qrCodeContent
private fun parseRecommendationCode(data: Any?): Pair<String, Long>? {
if (data == null) return null
return when (data) {
is String -> {
if (data.isEmpty()) null
else Pair(data, System.currentTimeMillis() + expireTimeInMillis)
}
else -> {
val bean = Gson().fromJson(Gson().toJson(data), RecommendationCodeBean::class.java)
val code = bean.inviteCode?.takeIf { it.isNotEmpty() }
?: bean.code?.takeIf { it.isNotEmpty() }
?: return null
val expire = bean.expireTime ?: bean.expire
?: (System.currentTimeMillis() + expireTimeInMillis)
Pair(code, expire)
}
}
}
/*
* 这段代码生成带Logo的二维码
@@ -232,7 +428,7 @@ class InvitationCodeActivity : AppCompatActivity() {
}
}
val logoDrawable = ContextCompat.getDrawable(this, R.mipmap.loge)
val logoDrawable = ContextCompat.getDrawable(this, R.mipmap.logo)
if (logoDrawable != null) {
val logoSize = width / 5
val logoX = (width - logoSize) / 2
@@ -415,56 +611,62 @@ class InvitationCodeActivity : AppCompatActivity() {
/**
* 撤销邀请
*/
private fun cancelInvitation() {
// 撤销邀请逻辑
hideQrCode()
generatedInviteCode = ""
qrCodeContent = ""
expireTimestamp = 0
hasGeneratedQrCode = false
// 恢复按钮文字为"生成邀请码"
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
// 显示提示
ToastUtils.showSuccess(this, getString(R.string.operation_success))
}
/**
* 显示密码输入对话框
* @param onSuccess 验证成功后的回调
*/
private fun showPasswordDialog(onSuccess: () -> Unit) {
val dialog = PasswordInputDialog(this)
dialog.setTitle(getString(R.string.input_op_pwd))
dialog.setOnConfirmListener { password ->
verifyPassword(password, dialog, onSuccess)
private fun cancelInvitation(password: String, passwordDialog: PasswordInputDialog) {
if (isLoading) return
if (generatedInviteCode.isEmpty()) {
passwordDialog.showError(getString(R.string.qr_code_expired))
return
}
dialog.show()
}
isLoading = true
val loadingDialog = LoadingDialog(this).apply { show() }
/**
* 验证操作密码
*/
private fun verifyPassword(password: String, dialog: PasswordInputDialog, onSuccess: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.verifyOptionPassword(VerifyOptionPasswordRequest(password))
.revokeInvitation(VerifyOptionPasswordRequest(password))
Log.d(TAG, "撤销邀请响应: code=${response.code}, msg=${response.msg}")
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) {
dialog.dismiss()
onSuccess.invoke()
passwordDialog.dismiss()
hideQrCode()
generatedInviteCode = ""
qrCodeContent = ""
expireTimestamp = 0
hasGeneratedQrCode = false
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
} else {
dialog.showError(getString(R.string.password_error))
passwordDialog.showError(
response.msg.ifEmpty { getString(R.string.password_error) }
)
}
}
} catch (e: Exception) {
Log.e(TAG, "撤销邀请异常", e)
withContext(Dispatchers.Main) {
dialog.showError(getString(R.string.network_error))
isLoading = false
loadingDialog.dismiss()
passwordDialog.showError(getString(R.string.network_error))
}
}
}
}
/**
* 显示密码输入对话框
*/
private fun showPasswordDialog(onConfirm: (String, PasswordInputDialog) -> Unit) {
val dialog = PasswordInputDialog(this)
dialog.setTitle(getString(R.string.input_op_pwd))
dialog.setOnConfirmListener { password ->
onConfirm(password, dialog)
}
dialog.show()
}
override fun onDestroy() {
super.onDestroy()
expireTimeDialog?.dismiss()

View File

@@ -0,0 +1,123 @@
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.JoinTheFamilyBean
import com.ckkj.defense_device.databinding.JoinFamilyInviteActivityBinding
import com.ckkj.defense_device.home.HomeActivity
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.LoadingDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
// 邀请加入家庭
class JoinFamilyInviteActivity : AppCompatActivity() {
private lateinit var binding: JoinFamilyInviteActivityBinding
private var isLoading = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = JoinFamilyInviteActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
binding.ivBack.setOnClickListener { finish() }
binding.btnJoinNow.setOnClickListener { joinFamily() }
displayInviterInfo()
}
private fun joinFamily() {
if (isLoading) return
val parentId = intent.getStringExtra(EXTRA_USER_ID).orEmpty()
val inviteCode = intent.getStringExtra(EXTRA_INVITE_CODE).orEmpty()
if (parentId.isEmpty() || inviteCode.isEmpty()) {
Log.e(TAG, "加入家庭参数无效: parentId=$parentId, inviteCode=$inviteCode")
Toast.makeText(this, R.string.network_error, Toast.LENGTH_SHORT).show()
return
}
Log.e(TAG, "加入家庭请求: parentId=$parentId, inviteCode=$inviteCode")
isLoading = true
val loadingDialog = LoadingDialog(this).apply { show() }
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService()
.joinTheFamily(JoinTheFamilyBean(parentId = parentId, inviteCode = inviteCode))
Log.e(TAG, "加入家庭响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
if (response.code == 200) {
Log.e(TAG, "加入家庭成功,返回首页刷新")
ToastUtils.showSuccess(this@JoinFamilyInviteActivity, getString(R.string.operation_success))
val homeIntent = Intent(this@JoinFamilyInviteActivity, HomeActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
putExtra(HomeActivity.EXTRA_REFRESH, true)
}
startActivity(homeIntent)
finish()
} else {
Log.e(TAG, "加入家庭失败: code=${response.code}, msg=${response.msg}")
Toast.makeText(
this@JoinFamilyInviteActivity,
response.msg.ifBlank { getString(R.string.network_error) },
Toast.LENGTH_SHORT
).show()
}
}
} catch (e: Exception) {
Log.e(TAG, "加入家庭请求异常", e)
withContext(Dispatchers.Main) {
isLoading = false
loadingDialog.dismiss()
Toast.makeText(this@JoinFamilyInviteActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun displayInviterInfo() {
val nickName = intent.getStringExtra(EXTRA_NICK_NAME).orEmpty()
val avatar = intent.getStringExtra(EXTRA_AVATAR)
binding.tvNickname.text = nickName.ifEmpty { getString(R.string.nickname_placeholder) }
binding.tvInviteMessage.text = getString(
R.string.invite_join_family_message,
nickName.ifEmpty { getString(R.string.nickname_placeholder) }
)
val requestOptions = RequestOptions()
.placeholder(R.mipmap.img)
.error(R.mipmap.img)
Glide.with(this)
.load(avatar)
.apply(requestOptions)
.circleCrop()
.into(binding.ivAvatar)
}
companion object {
private const val TAG = "JoinFamilyInviteActivity"
const val EXTRA_INVITE_CODE = "invite_code"
const val EXTRA_NICK_NAME = "nick_name"
const val EXTRA_AVATAR = "avatar"
const val EXTRA_PARENT_ID = "parent_id"
const val EXTRA_USER_ID = "user_id"
}
}

View File

@@ -1,14 +1,9 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.LoginPasswordActivityBinding
import com.example.defenseapplication.databinding.PasswordManagementActivityBinding
import com.ckkj.defense_device.databinding.LoginPasswordActivityBinding
import com.gyf.immersionbar.ImmersionBar
//登录密码

View File

@@ -1,16 +1,16 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.UpdateUserInfo
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.example.defenseapplication.databinding.OperationPasswordActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.CustomDialogFragment
import com.example.defenseapplication.view.LoadingDialog
import com.example.defenseapplication.view.showDefenseDialog
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.UpdateUserInfo
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.databinding.OperationPasswordActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.CustomDialogFragment
import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,9 +1,9 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.databinding.PasswordManagementActivityBinding
import com.ckkj.defense_device.databinding.PasswordManagementActivityBinding
import com.gyf.immersionbar.ImmersionBar
//密码管理
class PasswordManagementActivity : AppCompatActivity() {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.Manifest
import android.content.ContentResolver
@@ -11,24 +11,21 @@ import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.example.defenseapplication.R
import com.example.defenseapplication.bean.UpdateUserInfo
import com.example.defenseapplication.bean.UserInfoBean
import com.example.defenseapplication.databinding.PersonalProfileActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.example.defenseapplication.view.AvatarSelectDialog
import com.example.defenseapplication.view.CustomDialogFragmentText
import com.example.defenseapplication.view.LoadingDialog
import com.example.defenseapplication.view.PhoneModifyDialogFragment
import com.ckkj.defense_device.R
import com.ckkj.defense_device.bean.UpdateUserInfo
import com.ckkj.defense_device.bean.UserInfoBean
import com.ckkj.defense_device.databinding.PersonalProfileActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.view.AvatarSelectDialog
import com.ckkj.defense_device.view.CustomDialogFragmentText
import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.PhoneModifyDialogFragment
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,13 +1,13 @@
package com.example.defenseapplication.personal
package com.ckkj.defense_device.personal
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import bean.ForgotRequest
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RetrievePasswordActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.RetrievePasswordActivityBinding
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import android.app.Activity
import java.lang.ref.WeakReference

View File

@@ -1,8 +1,8 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import android.annotation.SuppressLint
import android.util.Log
import com.example.defenseapplication.bean.BaseInfo
import com.ckkj.defense_device.bean.BaseInfo
import com.google.gson.Gson
import com.google.gson.JsonParser
import okhttp3.Interceptor
@@ -138,7 +138,7 @@ object ApiConfig {
*/
private fun buildFakeErrorResponse(request: Request, msg: String, code: Int = 1): Response {
val baseInfo = BaseInfo(
message = msg,
msg = msg,
code = code,
data = null,
)

View File

@@ -1,32 +1,36 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import android.R
import bean.ForgotRequest
import com.example.defenseapplication.bean.DeviceBean
import com.example.defenseapplication.bean.DeviceUserBean
import com.example.defenseapplication.bean.FamilyBean
import com.example.defenseapplication.bean.GetCodeBean
import com.ckkj.defense_device.bean.DeviceBean
import com.ckkj.defense_device.bean.DeviceUserBean
import com.ckkj.defense_device.bean.FamilyBean
import com.ckkj.defense_device.bean.GetCodeBean
import bean.LoginBean
import bean.LoginRequest
import bean.RegisterRequest
import com.example.defenseapplication.bean.AddDeviceBean
import com.example.defenseapplication.bean.AddDeviceUserRequest
import com.example.defenseapplication.bean.DeleteBean
import com.example.defenseapplication.bean.DeleteDeviceUserRequest
import com.example.defenseapplication.bean.DeviceAlarmResponse
import com.example.defenseapplication.bean.FamilyMembersBean
import com.example.defenseapplication.bean.MessageBean
import com.example.defenseapplication.bean.MessageListResponse
import com.example.defenseapplication.bean.ObtainInviterInformationBean
import com.example.defenseapplication.bean.ObtainInviterInformationRequest
import com.example.defenseapplication.bean.SwitchFamilyRequest
import com.example.defenseapplication.bean.UpdatePassword
import com.example.defenseapplication.bean.UpdateUserInfo
import com.example.defenseapplication.bean.UploadResponse
import com.example.defenseapplication.bean.UserInfoBean
import com.example.defenseapplication.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.bean.AddDeviceBean
import com.ckkj.defense_device.bean.AddDeviceUserRequest
import com.ckkj.defense_device.bean.DeleteBean
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
import com.ckkj.defense_device.bean.DeviceAlarmResponse
import com.ckkj.defense_device.bean.FamilyMembersBean
import com.ckkj.defense_device.bean.InviterInfoBean
import com.ckkj.defense_device.bean.JoinTheFamilyBean
import com.ckkj.defense_device.bean.MessageBean
import com.ckkj.defense_device.bean.MessageListResponse
import com.ckkj.defense_device.bean.ObtainInviterInformationBean
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
import com.ckkj.defense_device.bean.SelectInviterInfoBean
import com.ckkj.defense_device.bean.SwitchFamilyRequest
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
import com.ckkj.defense_device.bean.UpdatePassword
import com.ckkj.defense_device.bean.UpdateUserInfo
import com.ckkj.defense_device.bean.UploadResponse
import com.ckkj.defense_device.bean.UserInfoBean
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import okhttp3.MultipartBody
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
@@ -191,7 +195,7 @@ interface ApiService {
/**
* 获取邀请人信息
* 获取家庭成员信息
*/
@POST("/app/obtainInviterInformation")
suspend fun obtainInviterInformation(@Body request: ObtainInviterInformationRequest): GetCodeBean<ObtainInviterInformationBean>
@@ -236,15 +240,36 @@ interface ApiService {
/**
* 查看告警消息
*/
//@GET("/app/appMessageInfo/{id}")
//suspend fun getMessageInfo(@retrofit2.http.Path("id") id: String): GetCodeBean<MessageBean>
@GET("/app/appMessageInfo/{id}")
suspend fun getMessageInfo(@retrofit2.http.Path("id") id: String): GetCodeBean<MessageBean>
/**
* 删除告警消息
*/
@DELETE("/app/deleteMessage/{id}")
suspend fun deleteMessage(@retrofit2.http.Path("id") id: String): GetCodeBean<Any>
/**
* 修改家庭用户状态
*/
//@POST("/app/updateFamilyUser")
@POST("/app/updateFamilyUser")
suspend fun updateFamilyUser(@Body request: UpdateFamilyUserRequest): GetCodeBean<Any>
/**
* 获取邀请人信息
*/
@POST("/app/selectInviterInfo")
suspend fun selectInviterInfo(@Body request: SelectInviterInfoBean): GetCodeBean<InviterInfoBean>
/**
* 加入xxx家庭
*/
@POST("/app/joinTheFamily")
suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean<Any>
}

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
object DeviceIdEvent {
private val listeners = mutableListOf<DeviceIdListener>()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
object DeviceUpdateEvent {
private val listeners = mutableListOf<DeviceUpdateListener>()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
object DeviceUserUpdateEvent {
private val listeners = mutableListOf<DeviceUserUpdateListener>()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
object FamilySwitchEvent {
private val listeners = mutableListOf<FamilySwitchListener>()

View File

@@ -1,6 +1,4 @@
package com.example.defenseapplication.utils
import android.content.Context
package com.ckkj.defense_device.utils
/**
* HTTP 请求头的语言值管理统一由 LanguageUtil 提供

View File

@@ -1,8 +1,6 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Configuration
import android.os.Build
import android.os.LocaleList
import java.util.Locale

View File

@@ -1,7 +1,7 @@
package com.smart.data.repository
import com.example.defenseapplication.utils.ApiService
import com.example.defenseapplication.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.ApiService
import com.ckkj.defense_device.utils.RetrofitNetwork
class MainRepository(val apiService: ApiService = RetrofitNetwork.getNetworkService()) {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
object MessageStatusManager {
private var unreadCount = 0

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import com.google.gson.Gson
import okhttp3.OkHttpClient
@@ -13,6 +13,7 @@ object RetrofitNetwork {
private const val DEFAULT_TIMEOUT = 30L
const val BASE_URL = "https://stem-boozy-margarine.ngrok-free.dev"
//const val BASE_URL = "http://service.reinkun.com:8001"
private var retrofit: Retrofit? = null
private val gson = Gson()

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.utils
package com.ckkj.defense_device.utils
import bean.LoginBean
import com.blankj.utilcode.util.SPUtils

View File

@@ -1,12 +1,12 @@
package com.example.defenseapplication.view
package com.ckkj.defense_device.view
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.WindowManager
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.DialogAutoModeNoticeBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.DialogAutoModeNoticeBinding
import java.util.Calendar
class AutoModeNoticeDialog(context: Context) : Dialog(context) {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.view
package com.ckkj.defense_device.view
import android.app.Dialog
import android.content.Context
@@ -6,10 +6,8 @@ import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.DialogAvatarSelectBinding
import com.ckkj.defense_device.databinding.DialogAvatarSelectBinding
class AvatarSelectDialog(context: Context) {

View File

@@ -1,4 +1,4 @@
package com.example.defenseapplication.view
package com.ckkj.defense_device.view
import android.app.Dialog
import android.content.Context
@@ -6,10 +6,9 @@ import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.DialogContainerSelectBinding
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.DialogContainerSelectBinding
class ContainerSelectDialog(context: Context) {

Some files were not shown because too many files have changed in this diff Show More