Compare commits

...

10 Commits

Author SHA1 Message Date
ab0dd2fbf6 修改告警记录 2026-07-14 17:35:34 +08:00
a9a69ca31b 自定义下拉刷新控件 2026-07-10 17:14:55 +08:00
1d54d6aa2a logd加载动画优化 2026-07-10 10:42:28 +08:00
200086301a 优化布局,删除不需要的view背景 2026-07-09 14:14:59 +08:00
0d0838d0e1 注销确定弹窗显示 2026-07-07 16:19:21 +08:00
a132cdfd27 注销按钮优化 2026-07-07 15:31:09 +08:00
96c8cca033 个人碎片页面的注销账号和设置页面 2026-07-07 09:15:33 +08:00
07f634ae05 直播流开始和停止 2026-07-03 17:58:51 +08:00
4819dbce86 云台控制攻击 2026-07-03 11:30:41 +08:00
6942abca3b 云台控制 2026-07-01 17:59:23 +08:00
40 changed files with 1808 additions and 69 deletions

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-tsmarker3368904538941318214.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-tsmarker2940944726750836786.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-tsmarker13384034999995465012.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-tsmarker11134052769962658558.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-tsmarker4236714666597388016.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-tsmarker12754528945444950713.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

@@ -28,6 +28,12 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.DefenseApplication"> android:theme="@style/Theme.DefenseApplication">
<activity
android:name=".personal.CancelAccountActivity"
android:exported="false" />
<activity
android:name=".personal.SettingsMenusActivity"
android:exported="false" />
<activity <activity
android:name=".home.StartActivity" android:name=".home.StartActivity"
android:exported="true"> android:exported="true">
@@ -122,8 +128,7 @@
android:exported="false" /> android:exported="false" />
<activity <activity
android:name=".home.HomeActivity" android:name=".home.HomeActivity"
android:exported="false"> android:exported="false" />
</activity>
<activity <activity
android:name=".login.RegisterSuccessActivity" android:name=".login.RegisterSuccessActivity"
android:exported="false" /> android:exported="false" />

View File

@@ -0,0 +1,20 @@
package com.ckkj.defense_device.bean
data class VideoStreamInfo(
val deviceNo: String,
val streamId: String,
val app: String,
val online: Boolean,
val pushUrl: String,
val playToken: String,
val expireAt: String,
val playUrls:playUrls,
val expireSeconds: Int,
)
class playUrls(
val flv: String,
val hls: String,
val rtmp: String,
val rtsp: String,
val webrtc: String,
)

View File

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

View File

@@ -39,6 +39,7 @@ import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.view.DefenseDialogs import com.ckkj.defense_device.view.DefenseDialogs
import com.ckkj.defense_device.view.LoadingDialog import com.ckkj.defense_device.view.LoadingDialog
import com.ckkj.defense_device.view.PullRefreshLayout
import com.ckkj.defense_device.view.showDefenseDialog import com.ckkj.defense_device.view.showDefenseDialog
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager import com.maning.mlkitscanner.scan.MNScanManager
@@ -62,6 +63,9 @@ class HomeMenuFragment : Fragment() {
private val pageSize = 10 private val pageSize = 10
private var hasMore = true private var hasMore = true
private var isLoading = false private var isLoading = false
private var loadingDialog: LoadingDialog? = null
private var lastNoMoreToastTime: Long = 0L
private val noMoreToastInterval: Long = 1500L
private val requestPermissionsLauncher = registerForActivityResult( private val requestPermissionsLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions() ActivityResultContracts.RequestMultiplePermissions()
@@ -162,7 +166,7 @@ class HomeMenuFragment : Fragment() {
} }
private fun fetchInviterInfo(inviteCode: String) { private fun fetchInviterInfo(inviteCode: String) {
val loadingDialog = LoadingDialog(requireActivity()).apply { show() } val loadingDialog = LoadingDialog(requireActivity()).setMessage(this.getString(R.string.loading)).apply { show() }
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = RetrofitNetwork.getNetworkService() val response = RetrofitNetwork.getNetworkService()
@@ -297,9 +301,11 @@ class HomeMenuFragment : Fragment() {
) )
} }
binding.swipeRefreshLayout.setOnRefreshListener { binding.swipeRefreshLayout.setOnRefreshListener(object : PullRefreshLayout.OnRefreshListener {
override fun onRefresh() {
refreshDeviceList() refreshDeviceList()
} }
})
binding.rvDeviceList.addOnScrollListener(object : RecyclerView.OnScrollListener() { binding.rvDeviceList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@@ -307,9 +313,24 @@ class HomeMenuFragment : Fragment() {
val layoutManager = recyclerView.layoutManager as GridLayoutManager val layoutManager = recyclerView.layoutManager as GridLayoutManager
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition() val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
val totalItemCount = layoutManager.itemCount val totalItemCount = layoutManager.itemCount
val firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
if (firstVisibleItemPosition == 0) {
val firstChild = layoutManager.findViewByPosition(0)
firstChild?.let { child ->
val top = child.top
if (top >= 0) {
val offset = (top.toFloat() / dpToPx(80f)).coerceIn(0f, 1f)
binding.gradientLayout.translationY = -offset * dpToPx(20f)
}
}
}
if (!isLoading && hasMore && lastVisibleItemPosition >= totalItemCount - 1 && dy > 0) { if (!isLoading && hasMore && lastVisibleItemPosition >= totalItemCount - 1 && dy > 0) {
loadMoreDevices() loadMoreDevices()
} else if (!hasMore && !isLoading && lastVisibleItemPosition >= totalItemCount - 1
&& dy > 0 && totalItemCount > 0) {
showNoMoreToast()
} }
} }
}) })
@@ -425,7 +446,7 @@ class HomeMenuFragment : Fragment() {
canViewDevice = userInfo?.role == "appadmin" && familyBean.role != "member" canViewDevice = userInfo?.role == "appadmin" && familyBean.role != "member"
updateAdminUI() updateAdminUI()
updateFamilyDisplayText(familyName) updateFamilyDisplayText(familyName)
fetchDeviceList() fetchDeviceList(showLoading = true)
FamilySwitchEvent.notifyFamilySwitched(familyName) FamilySwitchEvent.notifyFamilySwitched(familyName)
} else { } else {
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
@@ -439,15 +460,20 @@ class HomeMenuFragment : Fragment() {
} }
} }
private fun fetchDeviceList(page: Int = 1, isLoadMore: Boolean = false) { private fun fetchDeviceList(page: Int = 1, isLoadMore: Boolean = false, showLoading: Boolean = false) {
if (!canViewDevice || isLoading) return if (!canViewDevice || isLoading) return
isLoading = true isLoading = true
loadingDialog = if (showLoading) LoadingDialog(requireActivity()).setMessage("加载中...").apply { show() } else null
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = RetrofitNetwork.getNetworkService().getDeviceList() val response = RetrofitNetwork.getNetworkService().getDeviceList()
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (!isAdded) return@withContext
loadingDialog?.dismiss()
loadingDialog = null
Log.d("DeviceList", "code = ${response.code}, data = ${response.data}") Log.d("DeviceList", "code = ${response.code}, data = ${response.data}")
if (response.code == 200 && response.data != null) { if (response.code == 200 && response.data != null) {
val newDevices = response.data val newDevices = response.data
@@ -470,25 +496,36 @@ class HomeMenuFragment : Fragment() {
} }
} }
isLoading = false isLoading = false
binding.swipeRefreshLayout.finishRefresh()
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (!isAdded) return@withContext
loadingDialog?.dismiss()
loadingDialog = null
noNetwork() noNetwork()
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
isLoading = false isLoading = false
if (isLoadMore) { if (isLoadMore) {
hasMore = false hasMore = false
} }
binding.swipeRefreshLayout.finishRefresh()
} }
} }
} }
} }
private fun showNoMoreToast() {
val now = System.currentTimeMillis()
if (now - lastNoMoreToastTime < noMoreToastInterval) return
lastNoMoreToastTime = now
ToastUtils.showToast(requireContext(), getString(R.string.no_more_data))
}
private fun refreshDeviceList() { private fun refreshDeviceList() {
currentPage = 1 currentPage = 1
hasMore = true hasMore = true
fetchDeviceList(currentPage, false) fetchDeviceList(currentPage, false)
binding.swipeRefreshLayout.isRefreshing = false
} }
private fun loadMoreDevices() { private fun loadMoreDevices() {
@@ -548,6 +585,8 @@ class HomeMenuFragment : Fragment() {
val intent = Intent(requireContext(), LiveVideoActivity::class.java) val intent = Intent(requireContext(), LiveVideoActivity::class.java)
intent.putExtra("device_id", device.deviceId) intent.putExtra("device_id", device.deviceId)
intent.putExtra("device_name", device.name) intent.putExtra("device_name", device.name)
intent.putExtra("deviceNo", device.deviceNo)
intent.putExtra("light", device.light)
startActivity(intent) startActivity(intent)
} else { } else {
showDefenseDialog( showDefenseDialog(
@@ -564,6 +603,14 @@ class HomeMenuFragment : Fragment() {
).toInt() ).toInt()
} }
private fun dpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
resources.displayMetrics
)
}
private class GridHorizontalSpaceItemDecoration( private class GridHorizontalSpaceItemDecoration(
private val horizontalSpace: Int private val horizontalSpace: Int
) : RecyclerView.ItemDecoration() { ) : RecyclerView.ItemDecoration() {
@@ -586,6 +633,8 @@ class HomeMenuFragment : Fragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
loadingDialog?.dismiss()
loadingDialog = null
binding.rvFamilyList.adapter = null binding.rvFamilyList.adapter = null
binding.rvDeviceList.adapter = null binding.rvDeviceList.adapter = null
MessageStatusManager.removeListener(unreadCountChangeListener) MessageStatusManager.removeListener(unreadCountChangeListener)

View File

@@ -16,6 +16,7 @@ import com.ckkj.defense_device.liveVideo.LinkedUserActivity
import com.ckkj.defense_device.personal.FaceManagementActivity import com.ckkj.defense_device.personal.FaceManagementActivity
import com.ckkj.defense_device.personal.PasswordManagementActivity import com.ckkj.defense_device.personal.PasswordManagementActivity
import com.ckkj.defense_device.personal.PersonalProfileActivity import com.ckkj.defense_device.personal.PersonalProfileActivity
import com.ckkj.defense_device.personal.SettingsMenusActivity
import com.ckkj.defense_device.utils.AppLanguage import com.ckkj.defense_device.utils.AppLanguage
import com.ckkj.defense_device.utils.LanguageUtil import com.ckkj.defense_device.utils.LanguageUtil
import com.ckkj.defense_device.utils.RetrofitNetwork import com.ckkj.defense_device.utils.RetrofitNetwork
@@ -52,7 +53,7 @@ class PersonalFragment : Fragment() {
.load("") .load("")
.circleCrop() .circleCrop()
.into(binding.ivAvatar) .into(binding.ivAvatar)
binding.headerLayout.setOnClickListener { binding.ivAvatar.setOnClickListener {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
try { try {
val response = RetrofitNetwork.getNetworkService().getUserInfo() val response = RetrofitNetwork.getNetworkService().getUserInfo()
@@ -132,6 +133,10 @@ class PersonalFragment : Fragment() {
} }
dialog.show(childFragmentManager, "logout_dialog") dialog.show(childFragmentManager, "logout_dialog")
} }
binding.ivMenu.setOnClickListener {
val intent = Intent(requireContext(), SettingsMenusActivity::class.java)
startActivity(intent)
}
fetchCurrentFamily() fetchCurrentFamily()
return binding.root return binding.root
} }

View File

@@ -184,6 +184,7 @@ class AlarmLogActivity : AppCompatActivity() {
} catch (e: Exception) { } catch (e: Exception) {
Log.e("AlarmLogActivity", "请求异常: ${e.message}", e) Log.e("AlarmLogActivity", "请求异常: ${e.message}", e)
showAlarmListNetworkError()
} finally { } finally {
isLoading = false isLoading = false
Log.d("AlarmLogActivity", "请求结束") Log.d("AlarmLogActivity", "请求结束")
@@ -191,14 +192,22 @@ class AlarmLogActivity : AppCompatActivity() {
} }
} }
private fun showEmptyView() { private fun updateAlarmListState() {
if (alarmList.isEmpty()) {
binding.alarmListRecyclerView.visibility = View.GONE binding.alarmListRecyclerView.visibility = View.GONE
binding.emptyLayout.visibility = View.VISIBLE binding.layoutNoPermission.visibility = View.VISIBLE
binding.layoutNoNetwork.visibility = View.GONE
} else {
binding.alarmListRecyclerView.visibility = View.VISIBLE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
}
} }
private fun hideEmptyView() { private fun showAlarmListNetworkError() {
binding.alarmListRecyclerView.visibility = View.VISIBLE binding.alarmListRecyclerView.visibility = View.GONE
binding.emptyLayout.visibility = View.GONE binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
} }
private fun filterAlarmListByDate(selectedDate: Calendar) { private fun filterAlarmListByDate(selectedDate: Calendar) {
@@ -216,12 +225,11 @@ class AlarmLogActivity : AppCompatActivity() {
Log.d("AlarmLogActivity", "日期筛选完成,筛选后条数: ${alarmList.size}") Log.d("AlarmLogActivity", "日期筛选完成,筛选后条数: ${alarmList.size}")
if (alarmList.isEmpty()) { if (alarmList.isEmpty()) {
showEmptyView()
currentAlarmItem = null currentAlarmItem = null
} else { } else {
hideEmptyView()
currentAlarmItem = alarmList.firstOrNull() currentAlarmItem = alarmList.firstOrNull()
} }
updateAlarmListState()
} }
private fun updateDateDisplay() { private fun updateDateDisplay() {
@@ -407,7 +415,7 @@ class AlarmLogActivity : AppCompatActivity() {
adapter?.notifyDataSetChanged() adapter?.notifyDataSetChanged()
currentAlarmItem = alarmList.firstOrNull() currentAlarmItem = alarmList.firstOrNull()
if (alarmList.isEmpty()) { if (alarmList.isEmpty()) {
showEmptyView() showAlarmListNetworkError()
} }
} else { } else {
Toast.makeText(this@AlarmLogActivity, response.msg, Toast.LENGTH_SHORT).show() Toast.makeText(this@AlarmLogActivity, response.msg, Toast.LENGTH_SHORT).show()

View File

@@ -5,6 +5,7 @@ import android.content.res.Configuration
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.MotionEvent
import android.view.View import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@@ -20,8 +21,14 @@ import com.gyf.immersionbar.ImmersionBar
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.OrientationUtils import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import android.os.Handler
import android.os.Looper
import com.ckkj.defense_device.bean.VideoStreamInfo
import com.ckkj.defense_device.bean.VideoStreamRequest
import com.ckkj.defense_device.view.MyGSYVideoPlayerLive
class LiveVideoActivity : BaseActivity() { class LiveVideoActivity : BaseActivity() {
private lateinit var binding: LiveVideoActivityBinding private lateinit var binding: LiveVideoActivityBinding
@@ -30,6 +37,7 @@ class LiveVideoActivity : BaseActivity() {
private var isLightOn = false private var isLightOn = false
private var isConsoleVisible = false private var isConsoleVisible = false
private var deviceId: String? = null private var deviceId: String? = null
private var deviceNo: String? = null
private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf() private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf()
private var alarmAdapter: AlarmLogHorizontalAdapter? = null private var alarmAdapter: AlarmLogHorizontalAdapter? = null
private var currentPage = 1 private var currentPage = 1
@@ -39,6 +47,9 @@ class LiveVideoActivity : BaseActivity() {
private var isLoading = false private var isLoading = false
private var orientationUtils: OrientationUtils? = null private var orientationUtils: OrientationUtils? = null
private val ptzHandler = Handler(Looper.getMainLooper())
private var currentPtzDirection: String? = null
private var streamUrl: String? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -54,6 +65,8 @@ class LiveVideoActivity : BaseActivity() {
deviceId = intent.getStringExtra("device_id") deviceId = intent.getStringExtra("device_id")
val deviceName = intent.getStringExtra("device_name") val deviceName = intent.getStringExtra("device_name")
deviceNo = intent.getStringExtra("deviceNo")
val light = intent.getStringExtra("light")
if (!deviceName.isNullOrEmpty()) { if (!deviceName.isNullOrEmpty()) {
binding.tvTitle.text = deviceName binding.tvTitle.text = deviceName
} }
@@ -80,15 +93,41 @@ class LiveVideoActivity : BaseActivity() {
} }
private fun initVideoPlayer() { private fun initVideoPlayer() {
val videoUrl ="http://220.203.23.195:9090/live/test001.live.flv"
orientationUtils = OrientationUtils(this, binding.exoPlayContextId) orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
orientationUtils?.isEnable = true orientationUtils?.isEnable = true
hidePlayerControls()
fetchStreamUrl()
}
private fun fetchStreamUrl() {
val no = deviceNo ?: return
lifecycleScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().videoStart(VideoStreamRequest(no))
Log.d("直播流开始", "code=${response.code}, msg=${response.msg}")
if (response.code == 200 && response.data != null) {
//flv(http) 支持IjkPlayer最好
//hls(.m3u8) 支持ExoPlayer/Ijk都支持
//rtmp IjkPlayer支持Exo默认不支持
//rtsp IjkPlayer支持
//webrtcGSYVideoPlayer 不支持
streamUrl = response.data.playUrls.flv
Log.d("直播流开始", "获取地址:$streamUrl")
setupVideoPlayer(streamUrl!!)
} else {
Log.e("直播流开始", response.msg)
}
} catch (e: Exception) {
Log.e("直播流开始", "异常", e)
}
}
}
private fun setupVideoPlayer(videoUrl: String) {
GSYVideoOptionBuilder() GSYVideoOptionBuilder()
.setUrl(videoUrl) .setUrl(videoUrl)
.setCacheWithPlay(false) .setCacheWithPlay(false)
.setCacheWithPlay(false)
.setRotateViewAuto(true) .setRotateViewAuto(true)
.setLockLand(false) .setLockLand(false)
.setShowFullAnimation(true) .setShowFullAnimation(true)
@@ -103,19 +142,31 @@ class LiveVideoActivity : BaseActivity() {
override fun onQuitFullscreen(url: String?, vararg objects: Any?) { override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
super.onQuitFullscreen(url, *objects) super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo() orientationUtils?.backToProtVideo()
} }
}) })
.build(binding.exoPlayContextId) .build(binding.exoPlayContextId)
hidePlayerControls()
binding.exoPlayContextId.startPlayLogic() binding.exoPlayContextId.startPlayLogic()
binding.exoPlayContextId.fullscreenButton.setOnClickListener { binding.exoPlayContextId.fullscreenButton.setOnClickListener {
binding.exoPlayContextId.onPtzControlListener =
object : MyGSYVideoPlayerLive.OnPtzControlListener {
override fun onStartPtz(direction: String) {
val no = deviceNo ?: return
startPtz(deviceNo = no, direction = direction)
}
override fun onStopPtz() {
stopCurrentPtz()
}
override fun onShoot() {
deviceNo?.let { shoot(it) }
}
}
orientationUtils?.resolveByClick() orientationUtils?.resolveByClick()
binding.exoPlayContextId.startWindowFullscreen(this, true, true) binding.exoPlayContextId.startWindowFullscreen(this, true, true)
} }
} }
private fun hidePlayerControls() { private fun hidePlayerControls() {
binding.exoPlayContextId.backButton.visibility = View.GONE binding.exoPlayContextId.backButton.visibility = View.GONE
@@ -237,6 +288,114 @@ class LiveVideoActivity : BaseActivity() {
binding.ivVoice.setImageResource(R.mipmap.voice) binding.ivVoice.setImageResource(R.mipmap.voice)
} }
} }
binding.ivUp.setOnTouchListener { v, event ->
handlePtzTouch(
view = v,
event = event,
direction = "up",
action = { deviceNo }
)
}
binding.ivDown.setOnTouchListener { v, event ->
handlePtzTouch(
view = v,
event = event,
direction = "down",
action = { deviceNo }
)
}
binding.ivLeft.setOnTouchListener { v, event ->
handlePtzTouch(
view = v,
event = event,
direction = "left",
action = { deviceNo }
)
}
binding.ivRight.setOnTouchListener { v, event ->
handlePtzTouch(
view = v,
event = event,
direction = "right",
action = { deviceNo }
)
}
}
private fun handlePtzTouch(
view: View,
event: MotionEvent,
direction: String,
action: () -> String?
): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
view.isPressed = true
val deviceNo = action()
if (!deviceNo.isNullOrEmpty()) {
startPtz(deviceNo = deviceNo, direction = direction)
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
view.isPressed = false
stopCurrentPtz()
}
}
return true
}
private fun startPtz(deviceNo: String, direction: String) {
stopCurrentPtz()
currentPtzDirection = direction
lifecycleScope.launch {
try {
val response = when (direction) {
"up" -> RetrofitNetwork.getNetworkService().ptzUp(deviceNo)
"down" -> RetrofitNetwork.getNetworkService().ptzDown(deviceNo)
"left" -> RetrofitNetwork.getNetworkService().ptzLeft(deviceNo)
"right" -> RetrofitNetwork.getNetworkService().ptzRight(deviceNo)
else -> null
}
Log.d("LV", "PTZ $direction start response: ${response?.code}")
} catch (e: Exception) {
Log.e("LV", "PTZ $direction start failed", e)
}
}
}
private fun shoot(deviceNo: String) {
Log.d("LV", "btnCapture click, deviceNo=$deviceNo, duration=0")
lifecycleScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().ptzShoot(
deviceNo = deviceNo,
duration = "0"
)
Log.d("LV", "PTZ shoot response: code=${response.code}, msg=${response.msg}")
} catch (e: Exception) {
Log.e("LV", "PTZ shoot failed", e)
}
}
}
private fun stopCurrentPtz() {
val direction = currentPtzDirection ?: return
currentPtzDirection = null
lifecycleScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().ptzStop(deviceNo ?: return@launch)
Log.d("LV", "PTZ $direction stop response: ${response.code}")
} catch (e: Exception) {
Log.e("LV", "PTZ $direction stop failed", e)
}
}
} }
private fun fetchAlarmList(page: Int = 1) { private fun fetchAlarmList(page: Int = 1) {
@@ -264,14 +423,35 @@ class LiveVideoActivity : BaseActivity() {
} }
alarmList.addAll(newData) alarmList.addAll(newData)
alarmAdapter?.notifyDataSetChanged() alarmAdapter?.notifyDataSetChanged()
updateAlarmListState()
} catch (e: Exception) { } catch (e: Exception) {
Log.e("LiveVideoActivity", "请求告警列表失败", e)
hasMore = false hasMore = false
showAlarmListNetworkError()
} finally { } finally {
isLoading = false isLoading = false
} }
} }
} }
private fun updateAlarmListState() {
if (alarmList.isEmpty()) {
binding.alarmLogRecyclerView.visibility = View.GONE
binding.layoutNoPermission.visibility = View.VISIBLE
binding.layoutNoNetwork.visibility = View.GONE
} else {
binding.alarmLogRecyclerView.visibility = View.VISIBLE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.GONE
}
}
private fun showAlarmListNetworkError() {
binding.alarmLogRecyclerView.visibility = View.GONE
binding.layoutNoPermission.visibility = View.GONE
binding.layoutNoNetwork.visibility = View.VISIBLE
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
binding.exoPlayContextId.onVideoResume() binding.exoPlayContextId.onVideoResume()
@@ -284,7 +464,21 @@ class LiveVideoActivity : BaseActivity() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
ptzHandler.removeCallbacksAndMessages(null)
orientationUtils?.releaseListener() orientationUtils?.releaseListener()
binding.exoPlayContextId.release() binding.exoPlayContextId.release()
stopStream()
}
private fun stopStream() {
val no = deviceNo ?: return
GlobalScope.launch {
try {
val response = RetrofitNetwork.getNetworkService().videoStop(no)
Log.d("直播流停止", "videoStop response: code=${response.code}, msg=${response.msg}")
} catch (e: Exception) {
Log.e("直播流停止", "停止推流异常", e)
}
}
} }
} }

View File

@@ -0,0 +1,144 @@
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.ckkj.defense_device.R
import com.ckkj.defense_device.databinding.CancelAccountActivityBinding
import com.ckkj.defense_device.login.LoginActivity
import com.ckkj.defense_device.utils.RetrofitNetwork
import com.ckkj.defense_device.utils.UserinfoUtil
import com.ckkj.defense_device.view.CustomDialogFragment
import com.ckkj.defense_device.view.DefenseDialogs
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CancelAccountActivity : AppCompatActivity() {
private lateinit var binding: CancelAccountActivityBinding
private var countDownTimer: CountDownTimer? = null
private var phoneNumber: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = CancelAccountActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
getUserInfo()
initListener()
}
private fun getUserInfo() {
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService().getUserInfo()
withContext(Dispatchers.Main) {
if (response.code == 200 && response.data != null) {
phoneNumber = response.data?.phonenumber ?: ""
binding.tvPhoneNumber.text = maskPhoneNumber(phoneNumber)
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
Toast.makeText(this@CancelAccountActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun maskPhoneNumber(phone: String): String {
if (phone.length != 11) {
return phone
}
return phone.substring(0, 3) + "****" + phone.substring(7)
}
private fun initListener() {
binding.ivBack.setOnClickListener {
finish()
}
binding.btnSendCode.setOnClickListener {
if (phoneNumber.isEmpty()) {
Toast.makeText(this, R.string.get_user_info_fail, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (phoneNumber.length != 11) {
Toast.makeText(this, R.string.phone_format_invalid, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
sendVerificationCode(phoneNumber)
}
binding.btnCancelAccount.setOnClickListener {
DefenseDialogs.showDialog(
this,
config = CustomDialogFragment.Config(
title = getString(R.string.tip),
message = getString(R.string.confirm_cancel_account_user_account),
positiveText = getString(R.string.confirm),
negativeText = getString(R.string.cancel)
),
onPositiveClick = {
Toast.makeText(this@CancelAccountActivity, getString(R.string.cancel_account), Toast.LENGTH_SHORT).show()
}
)
}
}
private fun sendVerificationCode(phone: String) {
binding.btnSendCode.isEnabled = false
CoroutineScope(Dispatchers.IO).launch {
try {
val response = RetrofitNetwork.getNetworkService().getCode(phone)
withContext(Dispatchers.Main) {
if (response.code == 200) {
Toast.makeText(this@CancelAccountActivity, getString(R.string.code_sent_to, phone), Toast.LENGTH_SHORT).show()
startCountdown()
} else {
binding.btnSendCode.isEnabled = true
Toast.makeText(this@CancelAccountActivity, response.msg, Toast.LENGTH_SHORT).show()
}
}
} catch (e: Exception) {
withContext(Dispatchers.Main) {
binding.btnSendCode.isEnabled = true
Toast.makeText(this@CancelAccountActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
}
}
}
}
private fun startCountdown() {
countDownTimer = object : CountDownTimer(60000, 1000) {
override fun onTick(millisUntilFinished: Long) {
binding.btnSendCode.text = getString(R.string.countdown_retry, millisUntilFinished / 1000)
}
override fun onFinish() {
binding.btnSendCode.text = getString(R.string.resend_code)
binding.btnSendCode.isEnabled = true
countDownTimer = null
}
}.start()
}
override fun onDestroy() {
super.onDestroy()
countDownTimer?.cancel()
}
}

View File

@@ -0,0 +1,65 @@
package com.ckkj.defense_device.personal
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import com.ckkj.defense_device.databinding.SettingsMenusActivityBinding
import com.gyf.immersionbar.ImmersionBar
class SettingsMenusActivity : AppCompatActivity() {
private lateinit var binding: SettingsMenusActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = SettingsMenusActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.titleBar(binding.topBar)
.init()
val versionName = packageManager.getPackageInfo(packageName, 0).versionName
binding.tvVersion.text = versionName
setupClickListeners()
}
private fun setupClickListeners() {
// 返回按钮
binding.ivBack.setOnClickListener {
finish()
}
// 检查更新
binding.itemCheckUpdate.setOnClickListener {
Toast.makeText(this, "正在检查更新...", Toast.LENGTH_SHORT).show()
}
// 清除缓存
binding.itemClearCache.setOnClickListener {
Toast.makeText(this, "缓存已清除", Toast.LENGTH_SHORT).show()
binding.tvCacheSize.text = "0 KB"
}
// 用户协议
binding.itemUserAgreement.setOnClickListener {
}
// 隐私政策
binding.itemPrivacyPolicy.setOnClickListener {
}
// 注销账号
binding.itemLogout.setOnClickListener {
val intent = Intent(this, CancelAccountActivity::class.java)
startActivity(intent)
}
}
}

View File

@@ -28,6 +28,8 @@ import com.ckkj.defense_device.bean.UpdateUserInfo
import com.ckkj.defense_device.bean.UploadResponse import com.ckkj.defense_device.bean.UploadResponse
import com.ckkj.defense_device.bean.UserInfoBean import com.ckkj.defense_device.bean.UserInfoBean
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
import com.ckkj.defense_device.bean.VideoStreamInfo
import com.ckkj.defense_device.bean.VideoStreamRequest
import okhttp3.MultipartBody import okhttp3.MultipartBody
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.DELETE import retrofit2.http.DELETE
@@ -271,5 +273,79 @@ interface ApiService {
@POST("/app/joinTheFamily") @POST("/app/joinTheFamily")
suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean<Any> suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean<Any>
/**
* PTZ向上控制
*/
@POST("/app/ptz/{deviceNo}/up")
suspend fun ptzUp(
@retrofit2.http.Path("deviceNo") deviceNo: String,
@Query("speed") speed: String = "5",
@Query("duration") duration: String = "0"
): GetCodeBean<Any>
/**
* PTZ向下控制
*/
@POST("/app/ptz/{deviceNo}/down")
suspend fun ptzDown(
@retrofit2.http.Path("deviceNo") deviceNo: String,
@Query("speed") speed: String = "5",
@Query("duration") duration: String = "0"
): GetCodeBean<Any>
/**
* PTZ向左控制
*/
@POST("/app/ptz/{deviceNo}/left")
suspend fun ptzLeft(
@retrofit2.http.Path("deviceNo") deviceNo: String,
@Query("speed") speed: String = "5",
@Query("duration") duration: String = "0"
): GetCodeBean<Any>
/**
* PTZ向右控制
*/
@POST("/app/ptz/{deviceNo}/right")
suspend fun ptzRight(
@retrofit2.http.Path("deviceNo") deviceNo: String,
@Query("speed") speed: String = "5",
@Query("duration") duration: String = "0"
): GetCodeBean<Any>
/**
* PTZ停止控制
*/
@POST("/app/ptz/{deviceNo}/stop")
suspend fun ptzStop(
@retrofit2.http.Path("deviceNo") deviceNo: String,
): GetCodeBean<Any>
/**
* PTZ控制水枪喷水
*/
@POST("/app/ptz/{deviceNo}/shoot")
suspend fun ptzShoot(
@retrofit2.http.Path("deviceNo") deviceNo: String,
@Query("duration") duration: String,
): GetCodeBean<Any>
/**
* 开始推流
*/
@POST("/app/stream/start")
suspend fun videoStart(
@Body request: VideoStreamRequest
): GetCodeBean<VideoStreamInfo>
/**
* 停止推流
*/
@POST("/app/stream/stop/{deviceNo}")
suspend fun videoStop(
@retrofit2.http.Path("deviceNo") deviceNo: String
): GetCodeBean<Any>
} }

View File

@@ -12,8 +12,8 @@ import java.util.concurrent.TimeUnit
object RetrofitNetwork { object RetrofitNetwork {
private const val DEFAULT_TIMEOUT = 30L private const val DEFAULT_TIMEOUT = 30L
const val BASE_URL = "https://stem-boozy-margarine.ngrok-free.dev" //const val BASE_URL = "https://stem-boozy-margarine.ngrok-free.dev"
//const val BASE_URL = "http://service.reinkun.com:8001" const val BASE_URL = "http://service.reinkun.com:8001"
private var retrofit: Retrofit? = null private var retrofit: Retrofit? = null
private val gson = Gson() private val gson = Gson()

View File

@@ -4,6 +4,8 @@ import android.app.Dialog
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Handler
import android.os.Looper
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.WindowManager import android.view.WindowManager
@@ -13,6 +15,13 @@ class LoadingDialog(context: Context) {
private val dialog: Dialog = Dialog(context) private val dialog: Dialog = Dialog(context)
private val binding: DialogLoadingBinding private val binding: DialogLoadingBinding
private val handler = Handler(Looper.getMainLooper())
private var showTime: Long = 0
private val dismissRunnable = Runnable {
if (dialog.isShowing) {
dialog.dismiss()
}
}
init { init {
binding = DialogLoadingBinding.inflate(LayoutInflater.from(context)) binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
@@ -35,15 +44,24 @@ class LoadingDialog(context: Context) {
} }
fun show() { fun show() {
handler.removeCallbacks(dismissRunnable)
showTime = System.currentTimeMillis()
if (!dialog.isShowing) { if (!dialog.isShowing) {
dialog.show() dialog.show()
} }
} }
fun dismiss() { fun dismiss() {
handler.removeCallbacks(dismissRunnable)
val elapsed = System.currentTimeMillis() - showTime
val MIN_SHOW_TIME = 1000L
if (elapsed >= MIN_SHOW_TIME) {
if (dialog.isShowing) { if (dialog.isShowing) {
dialog.dismiss() dialog.dismiss()
} }
} else {
handler.postDelayed(dismissRunnable, MIN_SHOW_TIME - elapsed)
}
} }
fun isShowing(): Boolean { fun isShowing(): Boolean {

View File

@@ -1,8 +1,10 @@
package com.ckkj.defense_device.view package com.ckkj.defense_device.view
import android.util.Log
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import com.ckkj.defense_device.R import com.ckkj.defense_device.R
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
@@ -12,6 +14,18 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null context: Context, attrs: AttributeSet? = null
) : StandardGSYVideoPlayer(context, attrs) { ) : StandardGSYVideoPlayer(context, attrs) {
companion object {
private const val TAG = "MyGSYVideoPlayerLive"
}
interface OnPtzControlListener {
fun onStartPtz(direction: String)
fun onStopPtz()
fun onShoot()
}
var onPtzControlListener: OnPtzControlListener? = null
override fun init(context: Context) { override fun init(context: Context) {
super.init(context) super.init(context)
startButton.visibility = View.GONE startButton.visibility = View.GONE
@@ -76,10 +90,54 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor(
overlay.findViewById<View>(R.id.btnCapture) overlay.findViewById<View>(R.id.btnCapture)
?.setOnClickListener { ?.setOnClickListener {
Log.d("LV", "btnCapture clicked, onShoot invoked")
onPtzControlListener?.onShoot()
} }
player.initPtzControls(overlay)
return player return player
} }
private fun initPtzControls(overlay: View) {
val ptzListener = onPtzControlListener
overlay.findViewById<View>(R.id.ivUp)?.setOnTouchListener { v, event ->
handlePtzTouch(v, event, "up", ptzListener)
}
overlay.findViewById<View>(R.id.ivDown)?.setOnTouchListener { v, event ->
handlePtzTouch(v, event, "down", ptzListener)
}
overlay.findViewById<View>(R.id.ivLeft)?.setOnTouchListener { v, event ->
handlePtzTouch(v, event, "left", ptzListener)
}
overlay.findViewById<View>(R.id.ivRight)?.setOnTouchListener { v, event ->
handlePtzTouch(v, event, "right", ptzListener)
}
}
private fun handlePtzTouch(
view: View,
event: MotionEvent,
direction: String,
listener: OnPtzControlListener?
): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
Log.d(TAG, "全屏PTZ长按开始 direction=$direction")
view.isPressed = true
listener?.onStartPtz(direction)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
Log.d(TAG, "全屏PTZ长按结束 direction=$direction")
view.isPressed = false
listener?.onStopPtz()
}
}
return true
}
} }

View File

@@ -0,0 +1,288 @@
package com.ckkj.defense_device.view
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import android.widget.TextView
import com.ckkj.defense_device.R
/**
* 自定义下拉刷新控件
*下拉时列表跟随向下移动,顶部出现 Logo + 文字下拉头。
*
* 使用方式:直接包裹 RecyclerView 等可滚动子 View调用 [setOnRefreshListener] 监听刷新。
*/
class PullRefreshLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
/** 触发刷新的下拉阈值Header 的高度) */
private val headerHeight: Int = dp(120)
/** 触发刷新的最大下拉距离Header 高度的 1.5 倍) */
private val maxPullDistance: Int = (headerHeight * 1.5f).toInt()
private var headerView: View
private val ivRefreshIcon: ImageView
private val tvHint: TextView
/** 子内容 View通常是 RecyclerView */
private var contentView: View? = null
private var downY = 0f
private var isDragging = false
/** 当前下拉偏移量(>= 0 */
private var currentOffset = 0f
/** 是否处于刷新状态 */
private var isRefreshing = false
set(value) {
if (field == value) return
field = value
if (value) {
startLoadingAnimation()
} else {
stopLoadingAnimation()
}
}
private var refreshListener: OnRefreshListener? = null
private var resetAnimator: ValueAnimator? = null
init {
clipToPadding = false
// 添加 Header 视图
headerView = LayoutInflater.from(context).inflate(R.layout.layout_refresh_header, this, false)
addView(headerView)
ivRefreshIcon = headerView.findViewById(R.id.ivRefreshIcon)
tvHint = headerView.findViewById(R.id.tvRefreshHint)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
// 测量 Header
val headerHeightSpec = MeasureSpec.makeMeasureSpec(headerHeight, MeasureSpec.EXACTLY)
headerView.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
headerHeightSpec
)
// 测量内容 View仅第一个非 Header 子 View
for (i in 0 until childCount) {
val child = getChildAt(i)
if (child === headerView) continue
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
)
contentView = child
}
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
// Header 布局在容器顶部之上(初始隐藏,下拉时滑入)
headerView.layout(
0,
-headerHeight + currentOffset.toInt(),
width,
currentOffset.toInt()
)
// 内容 View 跟随偏移
contentView?.layout(
0,
currentOffset.toInt(),
width,
height + currentOffset.toInt()
)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
if (isRefreshing) return false
when (ev.actionMasked) {
MotionEvent.ACTION_DOWN -> {
downY = ev.y
isDragging = false
cancelResetAnimator()
}
MotionEvent.ACTION_MOVE -> {
val dy = ev.y - downY
if (dy > touchSlop && canPullDown()) {
isDragging = true
return true
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isDragging = false
}
}
return false
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
if (isRefreshing) return false
when (ev.actionMasked) {
MotionEvent.ACTION_MOVE -> {
val dy = ev.y - downY
if (dy > 0) {
if (!isDragging) {
if (dy > touchSlop) {
isDragging = true
} else {
return true
}
}
// 添加阻尼效果:越往下拉越吃力
val damped = computePullOffset(dy)
currentOffset = damped.coerceAtMost(maxPullDistance.toFloat())
updateHeaderState()
requestLayout()
return true
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
if (currentOffset >= headerHeight * 0.9f) {
// 触发刷新
triggerRefresh()
} else {
// 回弹
resetToIdle()
}
isDragging = false
return true
}
}
return isDragging
}
/**
* 添加阻尼效果,让下拉越深越慢,模拟弹性手感
*/
private fun computePullOffset(rawDy: Float): Float {
// 原始距离的 0.5 倍为基本偏移,超过阈值后逐渐衰减
val resistance = 1.8f
return (rawDy / resistance).coerceAtMost(maxPullDistance.toFloat())
}
/**
* 判断是否可以从当前位置下拉(列表已经滚动到顶部)
*/
private fun canPullDown(): Boolean {
val content = contentView ?: return false
return !content.canScrollVertically(-1)
}
/**
* 根据下拉距离更新 Header 的视觉状态(旋转角度、提示文字)
*/
private fun updateHeaderState() {
val ratio = (currentOffset / headerHeight).coerceIn(0f, 1f)
// 刷新图标根据下拉比例旋转 0~360 度
ivRefreshIcon.rotation = ratio * 360f
// 文字提示
tvHint.text = when {
currentOffset >= headerHeight * 0.9f -> context.getString(R.string.refresh_hint_release)
else -> context.getString(R.string.refresh_hint_pull)
}
}
private fun triggerRefresh() {
// 平滑动画到 header 高度
val animator = ValueAnimator.ofFloat(currentOffset, headerHeight.toFloat())
animator.duration = 200L
animator.interpolator = LinearInterpolator()
animator.addUpdateListener {
currentOffset = it.animatedValue as Float
requestLayout()
}
animator.start()
resetAnimator = animator
isRefreshing = true
tvHint.text = context.getString(R.string.refresh_hint_refreshing)
refreshListener?.onRefresh()
}
/**
* 结束刷新并回弹
*/
fun finishRefresh() {
if (!isRefreshing) return
isRefreshing = false
resetToIdle()
}
private fun resetToIdle() {
cancelResetAnimator()
val animator = ValueAnimator.ofFloat(currentOffset, 0f)
animator.duration = 300L
animator.interpolator = LinearInterpolator()
animator.addUpdateListener {
currentOffset = it.animatedValue as Float
if (!isRefreshing) {
// 刷新结束后,重置图标旋转角度
ivRefreshIcon.rotation = 0f
tvHint.text = context.getString(R.string.refresh_hint_pull)
}
requestLayout()
}
animator.start()
resetAnimator = animator
}
private fun cancelResetAnimator() {
resetAnimator?.cancel()
resetAnimator = null
}
private fun startLoadingAnimation() {
// 持续旋转动画
ivRefreshIcon.animate()
.rotation(ivRefreshIcon.rotation + 360f)
.setDuration(800L)
.setInterpolator(LinearInterpolator())
.withEndAction {
if (isRefreshing) startLoadingAnimation()
}
.start()
}
private fun stopLoadingAnimation() {
ivRefreshIcon.animate().cancel()
ivRefreshIcon.rotation = 0f
tvHint.text = context.getString(R.string.refresh_hint_pull)
}
fun setOnRefreshListener(listener: OnRefreshListener) {
this.refreshListener = listener
}
interface OnRefreshListener {
fun onRefresh()
}
private fun dp(value: Int): Int {
val density = resources.displayMetrics.density
return (value * density).toInt()
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#CC3D49" />
<corners android:radius="25dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="#E34D59" />
<corners android:radius="25dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#E34D59"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

View File

@@ -42,8 +42,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white" android:background="@color/white"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:paddingVertical="12dp" android:paddingVertical="12dp">
android:layout_marginTop="8dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -102,7 +102,7 @@
android:background="#FFFFFF" /> android:background="#FFFFFF" />
<LinearLayout <LinearLayout
android:id="@+id/emptyLayout" android:id="@+id/layoutNoPermission"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
@@ -116,6 +116,22 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/no_message" /> android:src="@drawable/no_message" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:background="#FFFFFF"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/no_network_img_name" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -36,10 +36,6 @@
android:layout_height="24dp" /> android:layout_height="24dp" />
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/face" />
<LinearLayout <LinearLayout
android:gravity="center_vertical" android:gravity="center_vertical"

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".personal.CancelAccountActivity">
<LinearLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:paddingHorizontal="16dp">
<ImageView
android:id="@+id/ivBack"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
android:contentDescription="@string/back" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/cancel_account"
android:textColor="@color/black"
android:textSize="16dp"
android:textStyle="bold" />
<View
android:layout_width="24dp"
android:layout_height="24dp" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/cancel_account_warning_text"
android:textColor="@color/gray"
android:textSize="14sp"
android:lineSpacingMultiplier="1.5" />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/gray_divider"
android:layout_marginVertical="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/current_account"
android:textColor="@color/text_secondary"
android:textSize="14sp" />
<TextView
android:id="@+id/tvPhoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginTop="8dp" />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/gray_divider"
android:layout_marginVertical="16dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/verification_code"
android:textColor="@color/text_secondary"
android:textSize="14sp" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="8dp"
android:background="@drawable/textinput_white_bg">
<EditText
android:id="@+id/etVerificationCode"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/btnSendCode"
android:hint="@string/enter_verification_code"
android:inputType="number"
android:paddingHorizontal="16dp"
android:textColor="@color/black"
android:background="@null"
android:textColorHint="@color/gray"
android:textSize="16sp" />
<android.widget.Button
android:id="@+id/btnSendCode"
android:layout_width="110dp"
android:layout_height="36dp"
android:minWidth="0dp"
android:minHeight="0dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
android:background="@drawable/send_code_btn_selector"
android:text="@string/send_sms_code"
android:textColor="@color/send_code_text_color"
android:elevation="0dp"
android:stateListAnimator="@null"
android:gravity="center"
android:textSize="14sp" />
</RelativeLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="@color/white">
<android.widget.Button
android:id="@+id/btnCancelAccount"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@drawable/btn_selector_red"
android:text="@string/cancel_account_btn"
android:textColor="@color/white"
android:elevation="0dp"
android:stateListAnimator="@null"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="@string/uploading" android:text="@string/uploading"
android:textColor="@color/black" android:textColor="@color/green"
android:textSize="16sp"/> android:textSize="16sp"/>
</LinearLayout> </LinearLayout>

View File

@@ -134,7 +134,7 @@
</LinearLayout> </LinearLayout>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <com.ckkj.defense_device.view.PullRefreshLayout
android:id="@+id/swipeRefreshLayout" android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_marginLeft="12dp" android:layout_marginLeft="12dp"
@@ -152,11 +152,11 @@
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="12dp" android:layout_marginEnd="12dp"
android:clipToPadding="false" android:clipToPadding="false"
android:nestedScrollingEnabled="false" android:overScrollMode="never"
android:paddingBottom="16dp" android:paddingBottom="16dp"
android:background="@android:color/transparent" android:background="@android:color/transparent"
tools:listitem="@layout/item_device" /> tools:listitem="@layout/item_device" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </com.ckkj.defense_device.view.PullRefreshLayout>
<LinearLayout <LinearLayout
android:id="@+id/layoutNoPermission" android:id="@+id/layoutNoPermission"

View File

@@ -40,7 +40,6 @@
android:id="@+id/llExpireTime" android:id="@+id/llExpireTime"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="56dp"
android:layout_marginTop="8dp"
android:background="@color/white" android:background="@color/white"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingHorizontal="16dp"> android:paddingHorizontal="16dp">

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingTop="24dp"
android:paddingBottom="20dp">
<ImageView
android:id="@+id/ivRefreshIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/setting" />
<TextView
android:id="@+id/tvRefreshHint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/refresh_hint_pull"
android:textColor="@color/green"
android:textSize="13sp" />
</LinearLayout>

View File

@@ -48,8 +48,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/white" android:background="@color/white"
android:paddingHorizontal="16dp" android:paddingHorizontal="16dp"
android:paddingVertical="12dp" android:paddingVertical="12dp">
android:layout_marginTop="8dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -252,6 +252,39 @@
android:background="#FFFFFF" android:background="#FFFFFF"
android:paddingTop="8dp" /> android:paddingTop="8dp" />
<LinearLayout
android:id="@+id/layoutNoPermission"
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_message" />
</LinearLayout>
<LinearLayout
android:id="@+id/layoutNoNetwork"
android:layout_width="match_parent"
android:layout_height="200dp"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="32dp"
android:background="@color/white"
android:visibility="gone">
<ImageView
android:layout_width="244dp"
android:layout_height="131dp"
android:src="@mipmap/no_network_img_name" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -70,6 +70,15 @@
android:textColor="#666666" android:textColor="#666666"
android:textSize="16dp" /> android:textSize="16dp" />
</LinearLayout> </LinearLayout>
<ImageView
android:layout_marginLeft="5dp"
android:id="@+id/ivMenu"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical|top"
android:src="@mipmap/settings"/>
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout

View File

@@ -35,18 +35,16 @@
android:layout_height="24dp" /> android:layout_height="24dp" />
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/face" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:text="@string/function_settings" android:text="@string/function_settings"
android:textColor="@color/gray" android:textColor="@color/black"
android:background="@color/message_bg"
android:gravity="bottom"
android:paddingStart="20dp"
android:paddingBottom="7dp"
android:textSize="14dp" /> android:textSize="14dp" />
<LinearLayout <LinearLayout
@@ -191,18 +189,16 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<View .
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/face" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:text="@string/basic_settings" android:text="@string/basic_settings"
android:textColor="@color/gray" android:textColor="@color/black"
android:background="@color/message_bg"
android:gravity="bottom"
android:paddingStart="20dp"
android:paddingBottom="7dp"
android:textSize="14dp" /> android:textSize="14dp" />
<LinearLayout <LinearLayout

View File

@@ -0,0 +1,227 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
tools:context=".personal.SettingsMenusActivity">
<!-- Top Toolbar -->
<LinearLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:paddingHorizontal="16dp">
<ImageView
android:id="@+id/ivBack"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="@string/settings_title"
android:textColor="@color/black"
android:textSize="16dp"
android:textStyle="bold" />
</LinearLayout>
<!-- Menu List Container -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@android:color/white">
<!-- 检查更新 -->
<LinearLayout
android:id="@+id/item_check_update"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/check_update"
android:textSize="15sp" />
<TextView
android:id="@+id/tv_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="#999999"
android:textSize="13sp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:src="@mipmap/right_back"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="16dp"
android:background="#F0F0F0" />
<!-- 清除缓存 -->
<LinearLayout
android:id="@+id/item_clear_cache"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/clear_cache"
android:textSize="15sp" />
<TextView
android:id="@+id/tv_cache_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="11Kb"
android:textColor="#999999"
android:textSize="13sp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:src="@mipmap/right_back"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="16dp"
android:background="#F0F0F0" />
<!-- 用户协议 -->
<LinearLayout
android:id="@+id/item_user_agreement"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/user_agreement"
android:textSize="15sp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/right_back"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="16dp"
android:background="#F0F0F0" />
<!-- 隐私政策 -->
<LinearLayout
android:id="@+id/item_privacy_policy"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/privacy_policy"
android:textSize="15sp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/right_back" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginStart="16dp"
android:background="#F0F0F0" />
<!-- 注销账号 -->
<LinearLayout
android:id="@+id/item_logout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/cancel_account"
android:textSize="15sp" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/right_back"/>
</LinearLayout>
</LinearLayout>
<!-- Bottom Contact Text -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tip_contact_us"
android:textColor="#999999"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="19927623@qq.com"
android:textColor="#4A90D9"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>

View File

@@ -35,11 +35,6 @@
android:layout_height="24dp" /> android:layout_height="24dp" />
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="@color/face" />
<LinearLayout <LinearLayout
android:id="@+id/itemDistance" android:id="@+id/itemDistance"
android:layout_width="match_parent" android:layout_width="match_parent"

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -501,4 +501,22 @@
<string name="password_update_success">密码修改成功</string> <string name="password_update_success">密码修改成功</string>
<string name="password_update_failed">密码修改失败</string> <string name="password_update_failed">密码修改失败</string>
<string name="update_failed">更新失败</string> <string name="update_failed">更新失败</string>
<string name="check_update">检查更新</string>
<string name="clear_cache">清除缓存</string>
<string name="cancel_account">注销账号</string>
<string name="tip_contact_us">如有问题,请联系我们:</string>
<string name="cancel_account_warning_text">注销后无法恢复,请谨慎操作</string>
<string name="current_account">当前账号</string>
<string name="cancel_account_btn">注销</string>
<string name="confirm_cancel_account_user_account">确定注销该用户吗?</string>
<!-- HomeMenuFragment 列表刷新 -->
<string name="no_more_data">没有更多了</string>
<string name="refreshing">正在刷新…</string>
<string name="refresh_hint_pull">下拉刷新</string>
<string name="refresh_hint_release">松开刷新</string>
<string name="refresh_hint_refreshing">正在刷新…</string>
</resources> </resources>

View File

@@ -23,4 +23,12 @@
<color name="gradientLayout1">#EBDCED</color> <color name="gradientLayout1">#EBDCED</color>
<color name="gradientLayout2">#CAF4FF</color> <color name="gradientLayout2">#CAF4FF</color>
<!-- Common Colors -->
<color name="primary">#09C2BD</color>
<color name="text_primary">#333333</color>
<color name="text_secondary">#666666</color>
<color name="card_background">#F8F8F8</color>
<color name="button_disabled">#CCCCCC</color>
<color name="warning_red">#E34D59</color>
</resources> </resources>

View File

@@ -31,9 +31,7 @@
<string name="confirm_password">Confirm Password</string> <string name="confirm_password">Confirm Password</string>
<string name="password_requirement">Password must be 620 characters</string> <string name="password_requirement">Password must be 620 characters</string>
<string name="agreement_prefix">I have read and agree to the</string> <string name="agreement_prefix">I have read and agree to the</string>
<string name="user_agreement">User Agreement</string>
<string name="and">and</string> <string name="and">and</string>
<string name="privacy_policy">Privacy Policy</string>
<string name="register_success">Registration Successful</string> <string name="register_success">Registration Successful</string>
<string name="registering">Registering...</string> <string name="registering">Registering...</string>
<string name="register_success_message">Your account has been created successfully</string> <string name="register_success_message">Your account has been created successfully</string>
@@ -507,4 +505,25 @@
<string name="password_update_success">Password updated successfully</string> <string name="password_update_success">Password updated successfully</string>
<string name="password_update_failed">Failed to update password</string> <string name="password_update_failed">Failed to update password</string>
<string name="update_failed">Update failed</string> <string name="update_failed">Update failed</string>
<!-- Settings Menus -->
<string name="check_update">Check for Updates</string>
<string name="clear_cache">Clear Cache</string>
<string name="cancel_account">Cancel Account</string>
<string name="tip_contact_us">If you have any questions, please contact us:</string>
<string name="user_agreement">User Agreement</string>
<string name="privacy_policy">Privacy Policy</string>
<!-- Cancel Account -->
<string name="cancel_account_warning_text">Account data cannot be restored after cancellation, please proceed with caution</string>
<string name="current_account">Current Account</string>
<string name="cancel_account_btn">Cancel</string>
<string name="confirm_cancel_account_user_account">Confirm Cancel Account User Account</string>
<!-- Home Menu refresh / load more -->
<string name="no_more_data">No more data</string>
<string name="refreshing">Refreshing…</string>
<string name="refresh_hint_pull">Pull down to refresh</string>
<string name="refresh_hint_release">Release to refresh</string>
<string name="refresh_hint_refreshing">Refreshing…</string>
</resources> </resources>