Compare commits
50 Commits
d30c025874
...
native2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fafefa3c38 | ||
|
|
a05cebae3f | ||
|
|
638233279f | ||
|
|
8b413c5a0c | ||
|
|
d79f43e2c9 | ||
|
|
8e237bd24a | ||
|
|
9f31f12463 | ||
|
|
55cf7196a6 | ||
|
|
fb99e7ecf4 | ||
| f154f30e2d | |||
| 7114e36165 | |||
| 195fb73620 | |||
| 655279a8a1 | |||
| 46385d5358 | |||
| ab0dd2fbf6 | |||
| a9a69ca31b | |||
| 1d54d6aa2a | |||
| 200086301a | |||
| 0d0838d0e1 | |||
| a132cdfd27 | |||
| 96c8cca033 | |||
| 07f634ae05 | |||
| 4819dbce86 | |||
| 6942abca3b | |||
| 02fc928f20 | |||
| b67de551ba | |||
| e28e167085 | |||
| 8cd802a826 | |||
| 7e14dd379a | |||
| 4a584fa7d7 | |||
| a32c165424 | |||
| 9177c00a3c | |||
| e937ce207c | |||
| 4462ba5ba2 | |||
| 0672d762c9 | |||
| efbba44250 | |||
| ef4dfb0307 | |||
| 086f1226a1 | |||
| 5cf30411c1 | |||
| 76e4949dd7 | |||
| 4a16010bd6 | |||
| b55d9a4b38 | |||
| 6a1cfaeda9 | |||
| f57975ef1e | |||
| 548d501a44 | |||
| a997190480 | |||
| 631f9831c3 | |||
| b2c3ed9015 | |||
| 6bd31327db | |||
| 7e362038b3 |
2
.idea/deploymentTargetSelector.xml
generated
2
.idea/deploymentTargetSelector.xml
generated
@@ -4,7 +4,7 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-05-18T09:18:19.023586Z">
|
||||
<DropdownSelection timestamp="2026-06-22T08:46:41.014465400Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=80bece4" />
|
||||
|
||||
48
.kotlin/errors/errors-1780537656129.log
Normal file
48
.kotlin/errors/errors-1780537656129.log
Normal file
@@ -0,0 +1,48 @@
|
||||
kotlin version: 2.2.10
|
||||
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker7700090853507788345.tmp
|
||||
|
||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
|
||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
|
||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
|
||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||
at org.gradle.internal.Factories$1.create(Factories.java:30)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
|
||||
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1780537656460.log
Normal file
48
.kotlin/errors/errors-1780537656460.log
Normal file
@@ -0,0 +1,48 @@
|
||||
kotlin version: 2.2.10
|
||||
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker12680940025563006602.tmp
|
||||
|
||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
|
||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
|
||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
|
||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||
at org.gradle.internal.Factories$1.create(Factories.java:30)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
|
||||
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1781138120653.log
Normal file
48
.kotlin/errors/errors-1781138120653.log
Normal file
@@ -0,0 +1,48 @@
|
||||
kotlin version: 2.2.10
|
||||
error message: java.nio.file.AccessDeniedException: C:\Users\Administrator\AppData\Local\kotlin\daemon\kotlin-daemon-client-tsmarker3894271637508823813.tmp
|
||||
|
||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:74)
|
||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:102)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
|
||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
|
||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
|
||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||
at org.gradle.internal.Factories$1.create(Factories.java:30)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
|
||||
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
|
||||
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
|
||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
|
||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
|
||||
at java.base/java.lang.Thread.run(Thread.java:1583)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1782700916835.log
Normal file
48
.kotlin/errors/errors-1782700916835.log
Normal 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-tsmarker16051875768763165132.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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1782787409833.log
Normal file
48
.kotlin/errors/errors-1782787409833.log
Normal 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-tsmarker3580585840179793220.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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1782788041845.log
Normal file
48
.kotlin/errors/errors-1782788041845.log
Normal 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-tsmarker11763075351170172587.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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1782888714708.log
Normal file
48
.kotlin/errors/errors-1782888714708.log
Normal 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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1782890222939.log
Normal file
48
.kotlin/errors/errors-1782890222939.log
Normal 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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1783384781223.log
Normal file
48
.kotlin/errors/errors-1783384781223.log
Normal 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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1783643668109.log
Normal file
48
.kotlin/errors/errors-1783643668109.log
Normal 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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1783645418169.log
Normal file
48
.kotlin/errors/errors-1783645418169.log
Normal 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)
|
||||
|
||||
|
||||
48
.kotlin/errors/errors-1783645640346.log
Normal file
48
.kotlin/errors/errors-1783645640346.log
Normal 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)
|
||||
|
||||
|
||||
@@ -36,6 +36,11 @@ android {
|
||||
buildFeatures {
|
||||
viewBinding=true
|
||||
}
|
||||
lint {
|
||||
disable += "ExpiredTargetSdkVersion"
|
||||
checkReleaseBuilds = false
|
||||
abortOnError = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -69,14 +74,8 @@ dependencies {
|
||||
// Kotlin 协程
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
|
||||
|
||||
// CameraX 用于相机预览
|
||||
implementation("androidx.camera:camera-camera2:1.3.0")
|
||||
implementation("androidx.camera:camera-lifecycle:1.3.0")
|
||||
implementation("androidx.camera:camera-view:1.3.0")
|
||||
// Google ML Kit 人脸检测
|
||||
implementation("com.google.mlkit:face-detection:16.1.5")
|
||||
// 图形绘制库(绘制人脸框)
|
||||
implementation("com.github.bumptech.glide:glide:4.15.1")
|
||||
//Glide 依赖
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||
// ZXing 二维码扫描库
|
||||
@@ -87,8 +86,6 @@ dependencies {
|
||||
implementation("com.geyifeng.immersionbar:immersionbar-ktx:3.2.2")
|
||||
implementation("com.wdullaer:materialdatetimepicker:4.2.3") // 经典稳定
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
// Android-PickerView 时间选择器
|
||||
implementation("com.contrarywind:Android-PickerView:4.1.9")
|
||||
//二维码三方库
|
||||
implementation("com.github.maning0303:MNMLKitScanner:V1.0.4")
|
||||
implementation("com.google.mlkit:barcode-scanning:17.0.2")
|
||||
|
||||
@@ -24,9 +24,16 @@
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/logo"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.DefenseApplication">
|
||||
<activity
|
||||
android:name=".personal.CancelAccountActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".personal.SettingsMenusActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".home.StartActivity"
|
||||
android:exported="true">
|
||||
@@ -42,6 +49,9 @@
|
||||
<activity
|
||||
android:name=".personal.InvitationCodeActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".personal.JoinFamilyInviteActivity"
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".liveVideo.StrikePositionActivity"
|
||||
android:exported="false" />
|
||||
@@ -118,8 +128,7 @@
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".home.HomeActivity"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
android:exported="false" />
|
||||
<activity
|
||||
android:name=".login.RegisterSuccessActivity"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -4,6 +4,26 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import com.blankj.utilcode.util.Utils
|
||||
import com.ckkj.defense_device.utils.LanguageUtil
|
||||
// _ooOoo_
|
||||
// o8888888o
|
||||
// 88" . "88
|
||||
// (| -_- |)
|
||||
// O\ = /O
|
||||
// ____/`---'\____
|
||||
// . ' \\| |// `.
|
||||
// / \\||| : |||// \
|
||||
// / _||||| -:- |||||- \
|
||||
// | | \\\ - /// | |
|
||||
// | \_| ''\---/'' | |
|
||||
// \ .-\__ `-` ___/-. /
|
||||
// ___`. .' /--.--\ `. . __
|
||||
// ."" '< `.___\_<|>_/___.' >'"".
|
||||
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
|
||||
// \ \ `-. \_ __\ /__ _/ .-` / /
|
||||
// ======`-.____`-.___\_____/___.-`____.-'======
|
||||
// `=---='
|
||||
//
|
||||
// 佛祖保佑 永无BUG
|
||||
|
||||
class MyApplication : Application() {
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ class AddDeviceAdapter(
|
||||
private val onItemClick: (Device) -> Unit
|
||||
) : RecyclerView.Adapter<AddDeviceAdapter.ViewHolder>() {
|
||||
|
||||
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val ivIcon: ImageView = itemView.findViewById(R.id.ivDeviceIcon2)
|
||||
val tvName: TextView = itemView.findViewById(R.id.tvDeviceName2)
|
||||
@@ -47,7 +49,9 @@ class AddDeviceAdapter(
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val device = devices[position]
|
||||
holder.tvName.text = device.name
|
||||
holder.ivIcon.setImageResource(R.mipmap.img)
|
||||
|
||||
val iconResId = defaultIcons[position % defaultIcons.size]
|
||||
holder.ivIcon.setImageResource(iconResId)
|
||||
|
||||
// 点击事件
|
||||
holder.itemView.setOnClickListener { onItemClick(device) }
|
||||
|
||||
@@ -3,25 +3,18 @@ package com.ckkj.defense_device.adapter
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.bean.DeviceBean
|
||||
import com.ckkj.defense_device.databinding.ItemDeviceBinding
|
||||
|
||||
data class DeviceUi(
|
||||
val name: String,
|
||||
val status: String,
|
||||
val deviceId: String = ""
|
||||
) {
|
||||
val isOnline: Boolean
|
||||
get() = status == "1"
|
||||
}
|
||||
|
||||
class DeviceAdapter(
|
||||
private val onItemClick: (DeviceUi) -> Unit = {}
|
||||
private val onItemClick: (DeviceBean) -> Unit = {}
|
||||
) : RecyclerView.Adapter<DeviceAdapter.DeviceViewHolder>() {
|
||||
|
||||
private val items = mutableListOf<DeviceUi>()
|
||||
private val items = mutableListOf<DeviceBean>()
|
||||
|
||||
fun submitList(deviceList: List<DeviceUi>) {
|
||||
fun submitList(deviceList: List<DeviceBean>) {
|
||||
items.clear()
|
||||
items.addAll(deviceList)
|
||||
notifyDataSetChanged()
|
||||
@@ -33,15 +26,25 @@ class DeviceAdapter(
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: DeviceViewHolder, position: Int) {
|
||||
holder.bind(items[position])
|
||||
holder.bind(items[position], position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
private val defaultIcons = listOf(R.mipmap.img1, R.mipmap.img2, R.mipmap.img3)
|
||||
|
||||
inner class DeviceViewHolder(private val binding: ItemDeviceBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: DeviceUi) {
|
||||
fun bind(item: DeviceBean, position: Int) {
|
||||
if (item.deviceImg.isNullOrEmpty()) {
|
||||
val iconResId = defaultIcons[position % defaultIcons.size]
|
||||
binding.ivDeviceIcon.setImageResource(iconResId)
|
||||
} else {
|
||||
Glide.with(binding.root.context)
|
||||
.load(item.deviceImg)
|
||||
.into(binding.ivDeviceIcon)
|
||||
}
|
||||
binding.tvDeviceType.text = item.name
|
||||
when (item.status) {
|
||||
"1" -> {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.ckkj.defense_device.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ckkj.defense_device.databinding.ItemWifiBinding
|
||||
|
||||
@@ -39,14 +40,25 @@ class WifiListAdapter(
|
||||
override fun getItemCount(): Int = wifiList.size
|
||||
|
||||
fun updateData(newList: List<String>) {
|
||||
val oldList = wifiList.toList()
|
||||
val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
|
||||
override fun getOldListSize() = oldList.size
|
||||
override fun getNewListSize() = newList.size
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
oldList[oldItemPosition] == newList[newItemPosition]
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
oldList[oldItemPosition] == newList[newItemPosition]
|
||||
})
|
||||
wifiList.clear()
|
||||
wifiList.addAll(newList)
|
||||
notifyDataSetChanged()
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
}
|
||||
|
||||
fun addData(newList: List<String>) {
|
||||
val startPosition = wifiList.size
|
||||
val newItems = newList.filter { !wifiList.contains(it) }
|
||||
if (newItems.isEmpty()) return
|
||||
wifiList.addAll(newItems)
|
||||
notifyItemRangeInserted(startPosition, newItems.size)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.ckkj.defense_device.bean
|
||||
* 统一返回数据基类(根据你的后端实际字段调整)
|
||||
*/
|
||||
data class BaseInfo(
|
||||
val message: String? = null,
|
||||
val msg: String? = null,
|
||||
val code: Int = 0,
|
||||
val data: Any? = null
|
||||
)
|
||||
@@ -9,11 +9,11 @@ data class DeviceBean(
|
||||
val deviceFamilyId: String? = null,
|
||||
val deviceImg: String? = null,
|
||||
val deviceName: String? = null,//设备名称
|
||||
val deviceNo: String? = null,//设备编号
|
||||
val deviceSn: String? = null,//设备序列号
|
||||
val deviceNo: String = "",//设备编号
|
||||
val deviceSn: String = "",//设备序列号
|
||||
val endTime: String? = null,//自动布放结束时间
|
||||
val expirationTime: String? = null,
|
||||
val fwVer: String? = null,//固件版本
|
||||
val fwVer: String= "",//固件版本
|
||||
val id: String? = null,
|
||||
val light: String? = null,//强光开启
|
||||
val patternPerception: String? = null,//识别模式
|
||||
@@ -27,4 +27,13 @@ data class DeviceBean(
|
||||
val wifiPassword: String? = null,//WiFi密码
|
||||
val workMode: String? = null,//工作模式 0-自动 1-半自动
|
||||
val optionPassword: String? = null, //操作密码
|
||||
)
|
||||
) {
|
||||
val deviceId: String
|
||||
get() = id ?: ""
|
||||
|
||||
val name: String
|
||||
get() = deviceName ?: ""
|
||||
|
||||
val isOnline: Boolean
|
||||
get() = status == "1"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
data class InviterInfoBean(
|
||||
val nickName: String? = null,
|
||||
val idCard: String? = null,
|
||||
val phonenumber: String? = null,
|
||||
val avatar: String? = null,
|
||||
val id: String? = null,
|
||||
val parentId: String? = null,
|
||||
val userId: String? = null,
|
||||
val mac: String? = null,
|
||||
val status: String? = null,
|
||||
) {
|
||||
fun resolveParentId(): String? =
|
||||
parentId?.takeIf { it.isNotBlank() }
|
||||
?: userId?.takeIf { it.isNotBlank() }
|
||||
?: id?.takeIf { it.isNotBlank() }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
|
||||
data class JoinTheFamilyBean(
|
||||
val parentId: String,
|
||||
val inviteCode: String,
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
data class RecommendationCodeBean(
|
||||
val inviteCode: String? = null,
|
||||
val code: String? = null,
|
||||
val expireTime: Long? = null,
|
||||
val expire: Long? = null
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
data class SelectInviterInfoBean(
|
||||
val code: String
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
data class UpdateFamilyUserRequest(
|
||||
val id: String,
|
||||
val optionPassword: String,
|
||||
val optionStatus: String,
|
||||
val status: String,
|
||||
)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.ckkj.defense_device.bean
|
||||
|
||||
data class VideoStreamRequest(
|
||||
val deviceNo: String
|
||||
)
|
||||
@@ -144,10 +144,6 @@ class AddFamilyActivity : AppCompatActivity() {
|
||||
}
|
||||
dialogBinding.rvWifiList.adapter = wifiListAdapter
|
||||
|
||||
dialogBinding.swipeRefreshLayout.setOnRefreshListener {
|
||||
refreshWifiList(dialogBinding)
|
||||
}
|
||||
|
||||
dialogBinding.rvWifiList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
@@ -168,16 +164,6 @@ class AddFamilyActivity : AppCompatActivity() {
|
||||
return rect.height()
|
||||
}
|
||||
|
||||
private fun refreshWifiList(dialogBinding: DialogWifiListBinding) {
|
||||
dialogBinding.swipeRefreshLayout.isRefreshing = true
|
||||
handler.postDelayed({
|
||||
wifiManager.startScan()
|
||||
val newWifiList = getWifiList()
|
||||
wifiListAdapter?.updateData(newWifiList)
|
||||
dialogBinding.swipeRefreshLayout.isRefreshing = false
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private fun loadMoreWifiList() {
|
||||
isLoadingMore = true
|
||||
handler.postDelayed({
|
||||
@@ -204,12 +190,8 @@ class AddFamilyActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateWifiList() {
|
||||
popupWindow?.contentView?.findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.rvWifiList)?.let { recyclerView ->
|
||||
val wifiList = getWifiList()
|
||||
recyclerView.adapter = WifiListAdapter(wifiList) { wifiName ->
|
||||
binding.etWifiName.setText(wifiName)
|
||||
popupWindow?.dismiss()
|
||||
}
|
||||
popupWindow?.contentView?.findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.rvWifiList)?.let {
|
||||
wifiListAdapter?.updateData(getWifiList())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.ckkj.defense_device.home
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.ckkj.defense_device.R
|
||||
@@ -7,6 +8,10 @@ import com.ckkj.defense_device.base.BaseActivity
|
||||
import com.ckkj.defense_device.databinding.HomeActivityBinding
|
||||
|
||||
class HomeActivity : BaseActivity() {
|
||||
|
||||
companion object {
|
||||
const val EXTRA_REFRESH = "extra_refresh"
|
||||
}
|
||||
// ViewBinding 核心对象
|
||||
private lateinit var binding: HomeActivityBinding
|
||||
|
||||
@@ -49,6 +54,28 @@ class HomeActivity : BaseActivity() {
|
||||
binding.ivProfile.setImageResource(R.mipmap.profile_checked)
|
||||
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_selected))
|
||||
}
|
||||
|
||||
handleRefreshIntent(intent)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
if (intent != null) {
|
||||
setIntent(intent)
|
||||
handleRefreshIntent(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRefreshIntent(intent: Intent) {
|
||||
if (intent.getBooleanExtra(EXTRA_REFRESH, false)) {
|
||||
intent.removeExtra(EXTRA_REFRESH)
|
||||
switchFragment(homeMenuFragment)
|
||||
binding.ivHome.setImageResource(R.mipmap.home_checked)
|
||||
binding.tvHome.setTextColor(resources.getColor(R.color.ic_selected))
|
||||
binding.ivProfile.setImageResource(R.mipmap.profile)
|
||||
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_normal))
|
||||
homeMenuFragment.refreshHomeData()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
@@ -21,15 +21,17 @@ import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.alibaba.sdk.android.emas.g
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.adapter.DeviceAdapter
|
||||
import com.ckkj.defense_device.adapter.DeviceUi
|
||||
import com.ckkj.defense_device.adapter.FamilyAdapter
|
||||
import com.ckkj.defense_device.bean.DeviceBean
|
||||
import com.ckkj.defense_device.bean.FamilyBean
|
||||
import com.ckkj.defense_device.bean.SwitchFamilyRequest
|
||||
import com.ckkj.defense_device.bean.SelectInviterInfoBean
|
||||
import com.ckkj.defense_device.databinding.HomeMenuFragmentBinding
|
||||
import com.ckkj.defense_device.liveVideo.LiveVideoActivity
|
||||
import com.ckkj.defense_device.personal.JoinFamilyInviteActivity
|
||||
import com.ckkj.defense_device.utils.DeviceIdEvent
|
||||
import com.ckkj.defense_device.utils.DeviceUpdateEvent
|
||||
import com.ckkj.defense_device.utils.FamilySwitchEvent
|
||||
@@ -37,6 +39,8 @@ import com.ckkj.defense_device.utils.MessageStatusManager
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.utils.UserinfoUtil
|
||||
import com.ckkj.defense_device.view.DefenseDialogs
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.ckkj.defense_device.view.PullRefreshLayout
|
||||
import com.ckkj.defense_device.view.showDefenseDialog
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import com.maning.mlkitscanner.scan.MNScanManager
|
||||
@@ -45,6 +49,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
//主页碎片
|
||||
class HomeMenuFragment : Fragment() {
|
||||
// ViewBinding
|
||||
@@ -59,18 +64,21 @@ class HomeMenuFragment : Fragment() {
|
||||
private val pageSize = 10
|
||||
private var hasMore = true
|
||||
private var isLoading = false
|
||||
private var loadingDialog: LoadingDialog? = null
|
||||
private var lastNoMoreToastTime: Long = 0L
|
||||
private val noMoreToastInterval: Long = 1500L
|
||||
|
||||
private val requestPermissionsLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
|
||||
|
||||
|
||||
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
|
||||
} else {
|
||||
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
|
||||
}
|
||||
|
||||
|
||||
if (cameraGranted && storageGranted) {
|
||||
startScan()
|
||||
} else {
|
||||
@@ -83,7 +91,7 @@ class HomeMenuFragment : Fragment() {
|
||||
requireContext(),
|
||||
Manifest.permission.CAMERA
|
||||
)
|
||||
|
||||
|
||||
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
@@ -96,7 +104,7 @@ class HomeMenuFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
|
||||
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
|
||||
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
|
||||
storagePermission == PackageManager.PERMISSION_GRANTED) {
|
||||
startScan()
|
||||
} else {
|
||||
@@ -122,23 +130,86 @@ class HomeMenuFragment : Fragment() {
|
||||
MNScanManager.RESULT_SUCCESS -> {
|
||||
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
|
||||
if (!result.isNullOrEmpty()) {
|
||||
ToastUtils.showToast(requireContext(), "扫码成功: $result")
|
||||
val inviteCode = parseInviteCode(result[0])
|
||||
if (inviteCode.isNullOrEmpty()) {
|
||||
Toast.makeText(requireContext(), "无效的邀请码", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
fetchInviterInfo(inviteCode)
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showToast(requireContext(), "扫码结果为空")
|
||||
Toast.makeText(requireContext(), "扫码结果为空", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
MNScanManager.RESULT_FAIL -> {
|
||||
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
|
||||
ToastUtils.showToast(requireContext(), "扫码失败: $error")
|
||||
Toast.makeText(requireContext(), "扫码失败: $error", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
MNScanManager.RESULT_CANCLE -> {
|
||||
ToastUtils.showToast(requireContext(), "扫码取消")
|
||||
Toast.makeText(requireContext(), "扫码取消", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun parseInviteCode(scanResult: String): String? {
|
||||
val trimmed = scanResult.trim()
|
||||
if (trimmed.isEmpty()) return null
|
||||
return try {
|
||||
if (trimmed.startsWith("{")) {
|
||||
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
|
||||
} else {
|
||||
trimmed
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
trimmed
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchInviterInfo(inviteCode: String) {
|
||||
val loadingDialog = LoadingDialog(requireActivity()).setMessage(this.getString(R.string.loading)).apply { show() }
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService()
|
||||
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
if (response.code == 200 && response.data != null) {
|
||||
val inviter = response.data
|
||||
val parentId = inviter.resolveParentId()
|
||||
if (parentId.isNullOrEmpty()) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
getString(R.string.network_error),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@withContext
|
||||
}
|
||||
val intent = Intent(requireContext(), JoinFamilyInviteActivity::class.java).apply {
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
|
||||
}
|
||||
startActivity(intent)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
response.msg.ifBlank { getString(R.string.network_error) },
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val unreadCountChangeListener = object : MessageStatusManager.UnreadCountChangeListener {
|
||||
override fun onUnreadCountChanged(count: Int) {
|
||||
updateUnreadCountUI(count)
|
||||
@@ -197,13 +268,8 @@ class HomeMenuFragment : Fragment() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// 为 LinearLayout 设置渐变背景
|
||||
setGradientBackground(
|
||||
view = binding.gradientLayout,
|
||||
startColor = Color.parseColor("#EBDCED"),
|
||||
endColor = Color.parseColor("#CAF4FF")
|
||||
)
|
||||
binding.message.setOnClickListener {
|
||||
binding.gradientLayout.setBackgroundColor(Color.TRANSPARENT)
|
||||
binding.message.setOnClickListener {
|
||||
val intent = Intent(requireContext(), MessageActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
@@ -236,9 +302,11 @@ class HomeMenuFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
refreshDeviceList()
|
||||
}
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(object : PullRefreshLayout.OnRefreshListener {
|
||||
override fun onRefresh() {
|
||||
refreshDeviceList()
|
||||
}
|
||||
})
|
||||
|
||||
binding.rvDeviceList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
@@ -246,9 +314,24 @@ class HomeMenuFragment : Fragment() {
|
||||
val layoutManager = recyclerView.layoutManager as GridLayoutManager
|
||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||
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) {
|
||||
loadMoreDevices()
|
||||
} else if (!hasMore && !isLoading && lastVisibleItemPosition >= totalItemCount - 1
|
||||
&& dy > 0 && totalItemCount > 0) {
|
||||
showNoMoreToast()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -285,6 +368,10 @@ class HomeMenuFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshHomeData() {
|
||||
fetchFamilyList()
|
||||
}
|
||||
|
||||
private fun fetchFamilyList() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
@@ -321,18 +408,22 @@ class HomeMenuFragment : Fragment() {
|
||||
Log.d("HomeMenuFragment", "canViewDevice = $canViewDevice")
|
||||
if (canViewDevice) {
|
||||
binding.tvDeviceTitle.visibility = View.VISIBLE
|
||||
binding.swipeRefreshLayout.visibility = View.VISIBLE
|
||||
binding.rvDeviceList.visibility = View.VISIBLE
|
||||
binding.message.visibility = View.VISIBLE
|
||||
binding.addFamily.visibility = View.VISIBLE
|
||||
binding.add.visibility = View.VISIBLE
|
||||
binding.layoutNoPermission.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.GONE
|
||||
} else {
|
||||
binding.tvDeviceTitle.visibility = View.GONE
|
||||
binding.swipeRefreshLayout.visibility = View.GONE
|
||||
binding.rvDeviceList.visibility = View.GONE
|
||||
binding.message.visibility = View.GONE
|
||||
binding.addFamily.visibility = View.GONE
|
||||
binding.add.visibility = View.GONE
|
||||
binding.layoutNoPermission.visibility = View.VISIBLE
|
||||
binding.layoutNoNetwork.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +447,7 @@ class HomeMenuFragment : Fragment() {
|
||||
canViewDevice = userInfo?.role == "appadmin" && familyBean.role != "member"
|
||||
updateAdminUI()
|
||||
updateFamilyDisplayText(familyName)
|
||||
fetchDeviceList()
|
||||
fetchDeviceList(showLoading = true)
|
||||
FamilySwitchEvent.notifyFamilySwitched(familyName)
|
||||
} else {
|
||||
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
|
||||
@@ -370,15 +461,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
|
||||
|
||||
isLoading = true
|
||||
|
||||
loadingDialog = if (showLoading) LoadingDialog(requireActivity()).setMessage(getString(R.string.loading)).apply { show() } else null
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService().getDeviceList()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!isAdded) return@withContext
|
||||
loadingDialog?.dismiss()
|
||||
loadingDialog = null
|
||||
Log.d("DeviceList", "code = ${response.code}, data = ${response.data}")
|
||||
if (response.code == 200 && response.data != null) {
|
||||
val newDevices = response.data
|
||||
@@ -390,32 +486,47 @@ class HomeMenuFragment : Fragment() {
|
||||
hasMore = newDevices.size >= pageSize
|
||||
}
|
||||
Log.d("DeviceList", "deviceBeanList size = ${deviceBeanList.size}")
|
||||
showDeviceContent()
|
||||
updateDeviceList()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
|
||||
noNetwork()
|
||||
val errorMsg = response.msg.ifBlank { getString(R.string.network_error) }
|
||||
Toast.makeText(requireContext(), errorMsg, Toast.LENGTH_SHORT).show()
|
||||
if (isLoadMore) {
|
||||
hasMore = false
|
||||
}
|
||||
}
|
||||
isLoading = false
|
||||
binding.swipeRefreshLayout.finishRefresh()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!isAdded) return@withContext
|
||||
loadingDialog?.dismiss()
|
||||
loadingDialog = null
|
||||
noNetwork()
|
||||
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
isLoading = false
|
||||
if (isLoadMore) {
|
||||
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() {
|
||||
currentPage = 1
|
||||
hasMore = true
|
||||
fetchDeviceList(currentPage, false)
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
private fun loadMoreDevices() {
|
||||
@@ -445,26 +556,38 @@ class HomeMenuFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDeviceList() {
|
||||
val deviceUiList = deviceBeanList.map {
|
||||
DeviceUi(it.deviceName ?: "", it.status ?: "", it.id ?: "")
|
||||
}
|
||||
private fun showDeviceContent() {
|
||||
binding.swipeRefreshLayout.visibility = View.VISIBLE
|
||||
binding.rvDeviceList.visibility = View.VISIBLE
|
||||
binding.layoutNoPermission.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (deviceUiList.isEmpty()) {
|
||||
private fun noNetwork() {
|
||||
binding.swipeRefreshLayout.visibility = View.GONE
|
||||
binding.rvDeviceList.visibility = View.GONE
|
||||
binding.layoutNoPermission.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun updateDeviceList() {
|
||||
if (deviceBeanList.isEmpty()) {
|
||||
deviceAdapter.submitList(listOf(
|
||||
DeviceUi(getString(R.string.no_device), "0")
|
||||
DeviceBean(deviceName = getString(R.string.no_device), status = "0")
|
||||
))
|
||||
} else {
|
||||
deviceAdapter.submitList(deviceUiList)
|
||||
deviceAdapter.submitList(deviceBeanList)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDeviceClicked(device: DeviceUi) {
|
||||
private fun onDeviceClicked(device: DeviceBean) {
|
||||
if (device.isOnline) {
|
||||
DeviceIdEvent.setDeviceId(device.deviceId)
|
||||
val intent = Intent(requireContext(), LiveVideoActivity::class.java)
|
||||
intent.putExtra("device_id", device.deviceId)
|
||||
intent.putExtra("device_name", device.name)
|
||||
intent.putExtra("deviceNo", device.deviceNo)
|
||||
intent.putExtra("light", device.light)
|
||||
startActivity(intent)
|
||||
} else {
|
||||
showDefenseDialog(
|
||||
@@ -481,6 +604,14 @@ class HomeMenuFragment : Fragment() {
|
||||
).toInt()
|
||||
}
|
||||
|
||||
private fun dpToPx(dp: Float): Float {
|
||||
return TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dp,
|
||||
resources.displayMetrics
|
||||
)
|
||||
}
|
||||
|
||||
private class GridHorizontalSpaceItemDecoration(
|
||||
private val horizontalSpace: Int
|
||||
) : RecyclerView.ItemDecoration() {
|
||||
@@ -501,19 +632,10 @@ class HomeMenuFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun setGradientBackground(
|
||||
view: View,
|
||||
startColor: Int,
|
||||
endColor: Int,
|
||||
orientation: GradientDrawable.Orientation = GradientDrawable.Orientation.TL_BR
|
||||
) {
|
||||
val gradientDrawable = GradientDrawable(orientation, intArrayOf(startColor, endColor))
|
||||
gradientDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT
|
||||
view.background = gradientDrawable
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
loadingDialog?.dismiss()
|
||||
loadingDialog = null
|
||||
binding.rvFamilyList.adapter = null
|
||||
binding.rvDeviceList.adapter = null
|
||||
MessageStatusManager.removeListener(unreadCountChangeListener)
|
||||
|
||||
@@ -5,11 +5,14 @@ import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.adapter.MessageListAdapter
|
||||
import com.ckkj.defense_device.adapter.MessageListItem
|
||||
import com.ckkj.defense_device.databinding.MessageActivityBinding
|
||||
import com.ckkj.defense_device.utils.MessageStatusManager
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.ckkj.defense_device.view.PullRefreshLayout
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -19,13 +22,13 @@ import kotlinx.coroutines.withContext
|
||||
class MessageActivity : AppCompatActivity() {
|
||||
private lateinit var binding: MessageActivityBinding
|
||||
private val messageAdapter by lazy { MessageListAdapter() }
|
||||
private lateinit var loadingDialog: LoadingDialog
|
||||
|
||||
private var currentPage = 1
|
||||
private val pageSize = 10
|
||||
private var isLoading = false
|
||||
private var hasMoreData = true
|
||||
private val allMessages = mutableListOf<com.ckkj.defense_device.bean.MessageBean>()
|
||||
private var lastLoadedCount = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -41,9 +44,18 @@ class MessageActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
loadingDialog = LoadingDialog(this).setMessage(getString(R.string.loading))
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
binding.rvMessageList.layoutManager = LinearLayoutManager(this)
|
||||
binding.rvMessageList.adapter = messageAdapter
|
||||
|
||||
binding.swipeRefreshLayout.setOnRefreshListener(object : PullRefreshLayout.OnRefreshListener {
|
||||
override fun onRefresh() {
|
||||
currentPage = 1
|
||||
hasMoreData = true
|
||||
loadMessageList()
|
||||
}
|
||||
})
|
||||
|
||||
binding.rvMessageList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
@@ -70,20 +82,21 @@ class MessageActivity : AppCompatActivity() {
|
||||
if (isLoading) return
|
||||
|
||||
isLoading = true
|
||||
|
||||
if (currentPage == 1) {
|
||||
showLoading()
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val messageResponse = RetrofitNetwork.getNetworkService().getMessage(
|
||||
pageNum = currentPage,
|
||||
pageSize = pageSize
|
||||
)
|
||||
println("total: ${messageResponse.total}")
|
||||
println("rows size: ${messageResponse.rows.size}")
|
||||
println("当前页:$currentPage, 加载条数:${messageResponse.rows.size}")
|
||||
|
||||
val newMessages = messageResponse.rows
|
||||
val loadedCount = newMessages.size
|
||||
|
||||
if (loadedCount < pageSize) {
|
||||
if (newMessages.size < pageSize) {
|
||||
hasMoreData = false
|
||||
}
|
||||
|
||||
@@ -98,11 +111,11 @@ class MessageActivity : AppCompatActivity() {
|
||||
allMessages.clear()
|
||||
}
|
||||
allMessages.addAll(newMessages)
|
||||
lastLoadedCount = loadedCount
|
||||
|
||||
val items = convertToMessageListItems(allMessages)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
hideLoading()
|
||||
hideEmptyState()
|
||||
messageAdapter.submitList(items)
|
||||
|
||||
@@ -116,20 +129,24 @@ class MessageActivity : AppCompatActivity() {
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (allMessages.isEmpty()) {
|
||||
println("items is empty, showing empty state")
|
||||
showEmptyState()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println("Exception: ${e.message}")
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (allMessages.isEmpty()) {
|
||||
showEmptyState()
|
||||
noNetwork()
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
isLoading = false
|
||||
if (currentPage == 1) {
|
||||
withContext(Dispatchers.Main) {
|
||||
hideLoading()
|
||||
binding.swipeRefreshLayout.finishRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,16 +158,34 @@ class MessageActivity : AppCompatActivity() {
|
||||
loadMessageList()
|
||||
}
|
||||
|
||||
private fun showLoading() {
|
||||
binding.swipeRefreshLayout.visibility = View.GONE
|
||||
binding.emptyLayout.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.GONE
|
||||
loadingDialog.show()
|
||||
}
|
||||
|
||||
private fun hideLoading() {
|
||||
binding.swipeRefreshLayout.visibility = View.VISIBLE
|
||||
loadingDialog.dismiss()
|
||||
}
|
||||
|
||||
private fun showEmptyState() {
|
||||
binding.rvMessageList.visibility = View.GONE
|
||||
binding.swipeRefreshLayout.visibility = View.GONE
|
||||
binding.emptyLayout.visibility = View.VISIBLE
|
||||
binding.tvNoMoreData.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun hideEmptyState() {
|
||||
binding.rvMessageList.visibility = View.VISIBLE
|
||||
binding.swipeRefreshLayout.visibility = View.VISIBLE
|
||||
binding.emptyLayout.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun noNetwork() {
|
||||
binding.swipeRefreshLayout.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.VISIBLE
|
||||
binding.tvNoMoreData.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun showNoMoreDataHint() {
|
||||
binding.tvNoMoreData.visibility = View.VISIBLE
|
||||
@@ -165,15 +200,12 @@ class MessageActivity : AppCompatActivity() {
|
||||
val groupedMessages = messages.groupBy { extractDate(it.createTime ?: "") }
|
||||
groupedMessages.forEach { (date, messageList) ->
|
||||
val headerTitle = formatDate(date)
|
||||
messageList.forEachIndexed { index, message ->
|
||||
println(" 消息${index + 1}: id=${message.id}, categoryName=${message.categoryName}, createTime=${message.createTime}")
|
||||
}
|
||||
items.add(MessageListItem.Header(headerTitle))
|
||||
messageList.forEach { message ->
|
||||
items.add(
|
||||
MessageListItem.Message(
|
||||
time = extractTime(message.createTime ?: ""),
|
||||
title = message.categoryName ?: "未知消息",
|
||||
title = message.categoryName ?: "",
|
||||
dateTime = message.createTime ?: "",
|
||||
unread = false
|
||||
)
|
||||
|
||||
@@ -16,13 +16,16 @@ import com.ckkj.defense_device.liveVideo.LinkedUserActivity
|
||||
import com.ckkj.defense_device.personal.FaceManagementActivity
|
||||
import com.ckkj.defense_device.personal.PasswordManagementActivity
|
||||
import com.ckkj.defense_device.personal.PersonalProfileActivity
|
||||
import com.ckkj.defense_device.personal.SettingsMenusActivity
|
||||
import com.ckkj.defense_device.utils.AppLanguage
|
||||
import com.ckkj.defense_device.utils.LanguageUtil
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.utils.UserinfoUtil
|
||||
import com.ckkj.defense_device.utils.ActivityManager
|
||||
import com.ckkj.defense_device.view.CustomDialogFragmentText
|
||||
import com.ckkj.defense_device.view.FamilySwitchDialog
|
||||
import com.ckkj.defense_device.view.LanguageDialog
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -33,6 +36,7 @@ class PersonalFragment : Fragment() {
|
||||
// ViewBinding
|
||||
private var _binding: PersonalFragmentBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private var loadingDialog: LoadingDialog? = null
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
fetchUserInfo()
|
||||
@@ -48,10 +52,10 @@ class PersonalFragment : Fragment() {
|
||||
.titleBar(binding.headerLayout)
|
||||
.init()
|
||||
Glide.with(this)
|
||||
.load(R.mipmap.img)
|
||||
.load("")
|
||||
.circleCrop()
|
||||
.into(binding.ivAvatar)
|
||||
binding.headerLayout.setOnClickListener {
|
||||
binding.ivAvatar.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService().getUserInfo()
|
||||
@@ -100,27 +104,40 @@ class PersonalFragment : Fragment() {
|
||||
}
|
||||
// 退出登录点击事件
|
||||
binding.btnLogout.setOnClickListener {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService().logout()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
UserinfoUtil.clearUserInfo()
|
||||
val intent = Intent(requireContext(), com.ckkj.defense_device.login.LoginActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
|
||||
val dialog = CustomDialogFragmentText()
|
||||
.setTitle(getString(R.string.tip))
|
||||
.setMessage(getString(R.string.logout_confirm))
|
||||
.setShowInput(false)
|
||||
.setNegativeText(getString(R.string.cancel))
|
||||
.setPositiveText(getString(R.string.confirm))
|
||||
.setOnConfirmListener {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService().logout()
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
UserinfoUtil.clearUserInfo()
|
||||
val intent = Intent(requireContext(), com.ckkj.defense_device.login.LoginActivity::class.java)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
requireActivity().finish()
|
||||
} else {
|
||||
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(requireContext(), getString(R.string.logout_fail), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
dialog.show(childFragmentManager, "logout_dialog")
|
||||
}
|
||||
binding.ivMenu.setOnClickListener {
|
||||
val intent = Intent(requireContext(), SettingsMenusActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
fetchCurrentFamily()
|
||||
return binding.root
|
||||
@@ -219,6 +236,7 @@ class PersonalFragment : Fragment() {
|
||||
|
||||
|
||||
private fun fetchUserInfo() {
|
||||
loadingDialog = LoadingDialog(requireContext()).setMessage(getString(R.string.loading)).apply { show() }
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService().getUserInfo()
|
||||
@@ -229,6 +247,11 @@ class PersonalFragment : Fragment() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog?.dismiss()
|
||||
loadingDialog = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +280,7 @@ class PersonalFragment : Fragment() {
|
||||
} else {
|
||||
getString(R.string.member)
|
||||
}
|
||||
binding.tvRoleTag.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,9 @@ class SetNameActivity : AppCompatActivity() {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (result.code == 200) {
|
||||
Toast.makeText(this@SetNameActivity, "设备添加成功", Toast.LENGTH_SHORT).show()
|
||||
startActivity(Intent(this@SetNameActivity, LiveVideoActivity::class.java))
|
||||
startActivity(Intent(this@SetNameActivity, HomeActivity::class.java).apply {
|
||||
putExtra(HomeActivity.EXTRA_REFRESH, true)
|
||||
})
|
||||
finish()
|
||||
} else {
|
||||
Toast.makeText(this@SetNameActivity, result.msg ?: "添加失败", Toast.LENGTH_SHORT).show()
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.ckkj.defense_device.liveVideo
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -95,19 +96,33 @@ class AddLinkedUserActivity : AppCompatActivity() {
|
||||
Toast.makeText(this@AddLinkedUserActivity, "暂无家庭成员数据", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
hide()
|
||||
Toast.makeText(this@AddLinkedUserActivity, "请求失败: ${response.msg}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("AddLinkedUserActivity", "网络请求异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.swipeRefresh.isRefreshing = false
|
||||
noNetwork()
|
||||
Toast.makeText(this@AddLinkedUserActivity, "网络请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hide() {
|
||||
binding.searchLayout.visibility = View.GONE
|
||||
binding.rvAddLinkedUser.visibility = View.GONE
|
||||
binding.butting?.visibility = View.GONE
|
||||
binding.layoutNoPermission?.visibility = View.VISIBLE
|
||||
}
|
||||
private fun noNetwork() {
|
||||
binding.searchLayout.visibility = View.GONE
|
||||
binding.rvAddLinkedUser.visibility = View.GONE
|
||||
binding.butting?.visibility = View.GONE
|
||||
binding.layoutNoNetwork?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun updateFamilyMemberList(familyMembersList: List<FamilyMembersBean>) {
|
||||
fullUserItemList.clear()
|
||||
familyMembersList.forEach { member ->
|
||||
|
||||
@@ -158,32 +158,24 @@ class AlarmLogActivity : AppCompatActivity() {
|
||||
|
||||
private fun fetchAlarmList() {
|
||||
val currentDeviceId = deviceId ?: return
|
||||
|
||||
Log.d("AlarmLogActivity", "=== 开始请求设备告警日志 ===")
|
||||
Log.d("AlarmLogActivity", "请求参数 - deviceId: $currentDeviceId")
|
||||
|
||||
isLoading = true
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
Log.d("AlarmLogActivity", "正在发送网络请求...")
|
||||
val response = RetrofitNetwork.getNetworkService().getDeviceAlarmList(
|
||||
deviceId = currentDeviceId,
|
||||
pageNum = "1",
|
||||
pageSize = "10"
|
||||
)
|
||||
|
||||
Log.d("AlarmLogActivity", "请求完成")
|
||||
Log.d("AlarmLogActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
|
||||
|
||||
val newData = response.rows
|
||||
originalAlarmList.clear()
|
||||
originalAlarmList.addAll(newData)
|
||||
filterAlarmListByDate(calendar)
|
||||
currentAlarmItem = alarmList.firstOrNull()
|
||||
Log.d("AlarmLogActivity", "已获取 ${newData.size} 条数据")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("AlarmLogActivity", "请求异常: ${e.message}", e)
|
||||
showAlarmListNetworkError()
|
||||
} finally {
|
||||
isLoading = false
|
||||
Log.d("AlarmLogActivity", "请求结束")
|
||||
@@ -191,14 +183,22 @@ class AlarmLogActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEmptyView() {
|
||||
binding.alarmListRecyclerView.visibility = View.GONE
|
||||
binding.emptyLayout.visibility = View.VISIBLE
|
||||
private fun updateAlarmListState() {
|
||||
if (alarmList.isEmpty()) {
|
||||
binding.alarmListRecyclerView.visibility = View.GONE
|
||||
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() {
|
||||
binding.alarmListRecyclerView.visibility = View.VISIBLE
|
||||
binding.emptyLayout.visibility = View.GONE
|
||||
private fun showAlarmListNetworkError() {
|
||||
binding.alarmListRecyclerView.visibility = View.GONE
|
||||
binding.layoutNoPermission.visibility = View.GONE
|
||||
binding.layoutNoNetwork.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun filterAlarmListByDate(selectedDate: Calendar) {
|
||||
@@ -214,14 +214,13 @@ class AlarmLogActivity : AppCompatActivity() {
|
||||
}
|
||||
adapter?.notifyDataSetChanged()
|
||||
Log.d("AlarmLogActivity", "日期筛选完成,筛选后条数: ${alarmList.size}")
|
||||
|
||||
|
||||
if (alarmList.isEmpty()) {
|
||||
showEmptyView()
|
||||
currentAlarmItem = null
|
||||
} else {
|
||||
hideEmptyView()
|
||||
currentAlarmItem = alarmList.firstOrNull()
|
||||
}
|
||||
updateAlarmListState()
|
||||
}
|
||||
|
||||
private fun updateDateDisplay() {
|
||||
@@ -407,7 +406,7 @@ class AlarmLogActivity : AppCompatActivity() {
|
||||
adapter?.notifyDataSetChanged()
|
||||
currentAlarmItem = alarmList.firstOrNull()
|
||||
if (alarmList.isEmpty()) {
|
||||
showEmptyView()
|
||||
showAlarmListNetworkError()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this@AlarmLogActivity, response.msg, Toast.LENGTH_SHORT).show()
|
||||
|
||||
@@ -31,6 +31,7 @@ class AutoModeActivity : AppCompatActivity() {
|
||||
private var selectedStartMinute = 0
|
||||
private var selectedEndHour = 10
|
||||
private var selectedEndMinute = 0
|
||||
private var optionPassword: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -42,6 +43,8 @@ class AutoModeActivity : AppCompatActivity() {
|
||||
.titleBar(binding.topBar)
|
||||
.init()
|
||||
|
||||
optionPassword = intent.getStringExtra("optionPassword")
|
||||
|
||||
initListener()
|
||||
|
||||
loadCurrentSettings()
|
||||
@@ -177,7 +180,8 @@ class AutoModeActivity : AppCompatActivity() {
|
||||
id = deviceId,
|
||||
workMode = selectedMode,
|
||||
startTime = startTime,
|
||||
endTime = endTime
|
||||
endTime = endTime,
|
||||
optionPassword = optionPassword
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
@@ -55,8 +55,8 @@ class DeviceInfoActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateDeviceInfo(device: com.ckkj.defense_device.bean.DeviceBean) {
|
||||
binding.tvDeviceModelValue.text = device.deviceNo.toString()
|
||||
binding.tvDeviceNoValue.text = device.deviceSn.toString()
|
||||
binding.tvFwVersionValue.text = device.fwVer.toString()
|
||||
binding.tvDeviceModelValue.text = device.deviceNo?.toString()?: ""
|
||||
binding.tvDeviceNoValue.text = device.deviceSn?.toString() ?: ""
|
||||
binding.tvFwVersionValue.text = device.fwVer?.toString()?: ""
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import com.ckkj.defense_device.utils.DeviceIdEvent
|
||||
import com.ckkj.defense_device.utils.DeviceUserUpdateEvent
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.CustomDialogFragmentText
|
||||
import com.ckkj.defense_device.view.PullRefreshLayout
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import com.yanzhenjie.recyclerview.SwipeMenuItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -95,9 +96,11 @@ class LinkedUserActivity : AppCompatActivity() {
|
||||
|
||||
(binding.rvLinkedUser as RecyclerView).layoutManager = LinearLayoutManager(this)
|
||||
|
||||
binding.swipeRefresh.setOnRefreshListener {
|
||||
fetchUserList()
|
||||
}
|
||||
binding.swipeRefresh.setOnRefreshListener(object : PullRefreshLayout.OnRefreshListener {
|
||||
override fun onRefresh() {
|
||||
fetchUserList()
|
||||
}
|
||||
})
|
||||
|
||||
binding.rvLinkedUser.setSwipeMenuCreator { leftMenu, rightMenu, position ->
|
||||
if (mode == MODE_SETTINGS) {
|
||||
@@ -229,7 +232,6 @@ class LinkedUserActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun fetchUserList() {
|
||||
binding.swipeRefresh.isRefreshing = true
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
var deviceId: String? = null
|
||||
@@ -238,7 +240,7 @@ class LinkedUserActivity : AppCompatActivity() {
|
||||
if (deviceId.isNullOrEmpty()) {
|
||||
Toast.makeText(this@LinkedUserActivity, "设备 ID 为空", Toast.LENGTH_SHORT).show()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.swipeRefresh.isRefreshing = false
|
||||
binding.swipeRefresh.finishRefresh()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
@@ -249,7 +251,7 @@ class LinkedUserActivity : AppCompatActivity() {
|
||||
RetrofitNetwork.getNetworkService().getFamilyMembers()
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.swipeRefresh.isRefreshing = false
|
||||
binding.swipeRefresh.finishRefresh()
|
||||
if (response.code == 200 && response.data != null) {
|
||||
if (mode == MODE_SETTINGS) {
|
||||
updateDeviceUserList(response.data as List<DeviceUserBean>)
|
||||
@@ -257,18 +259,32 @@ class LinkedUserActivity : AppCompatActivity() {
|
||||
updateFamilyMembersList(response.data as List<FamilyMembersBean>)
|
||||
}
|
||||
} else {
|
||||
hide()
|
||||
Toast.makeText(this@LinkedUserActivity, response.msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.swipeRefresh.isRefreshing = false
|
||||
Toast.makeText(this@LinkedUserActivity, "网络请求失败", Toast.LENGTH_SHORT).show()
|
||||
binding.swipeRefresh.finishRefresh()
|
||||
noNetwork()
|
||||
Toast.makeText(this@LinkedUserActivity, R.string.network_request_failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun hide() {
|
||||
binding.searchLayout.visibility = View.GONE
|
||||
binding.rvLinkedUser.visibility = View.GONE
|
||||
binding.butting?.visibility = View.GONE
|
||||
binding.layoutNoPermission?.visibility = View.VISIBLE
|
||||
}
|
||||
private fun noNetwork() {
|
||||
binding.searchLayout.visibility = View.GONE
|
||||
binding.rvLinkedUser.visibility = View.GONE
|
||||
binding.butting?.visibility = View.GONE
|
||||
binding.layoutNoNetwork?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun updateDeviceUserList(deviceUserList: List<DeviceUserBean>) {
|
||||
fullUserList.clear()
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -20,8 +21,14 @@ import com.gyf.immersionbar.ImmersionBar
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
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() {
|
||||
private lateinit var binding: LiveVideoActivityBinding
|
||||
@@ -30,6 +37,7 @@ class LiveVideoActivity : BaseActivity() {
|
||||
private var isLightOn = false
|
||||
private var isConsoleVisible = false
|
||||
private var deviceId: String? = null
|
||||
private var deviceNo: String? = null
|
||||
private var alarmList: MutableList<DeviceAlarmBean> = mutableListOf()
|
||||
private var alarmAdapter: AlarmLogHorizontalAdapter? = null
|
||||
private var currentPage = 1
|
||||
@@ -39,6 +47,9 @@ class LiveVideoActivity : BaseActivity() {
|
||||
private var isLoading = false
|
||||
|
||||
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?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -54,6 +65,8 @@ class LiveVideoActivity : BaseActivity() {
|
||||
|
||||
deviceId = intent.getStringExtra("device_id")
|
||||
val deviceName = intent.getStringExtra("device_name")
|
||||
deviceNo = intent.getStringExtra("deviceNo")
|
||||
val light = intent.getStringExtra("light")
|
||||
if (!deviceName.isNullOrEmpty()) {
|
||||
binding.tvTitle.text = deviceName
|
||||
}
|
||||
@@ -80,15 +93,41 @@ class LiveVideoActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private fun initVideoPlayer() {
|
||||
val videoUrl = "https://www.w3schools.com/html/movie.mp4"//"https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
|
||||
|
||||
orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
|
||||
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()
|
||||
.setUrl(videoUrl)
|
||||
.setCacheWithPlay(false)
|
||||
.setCacheWithPlay(false)
|
||||
.setRotateViewAuto(true)
|
||||
.setLockLand(false)
|
||||
.setShowFullAnimation(true)
|
||||
@@ -103,19 +142,31 @@ class LiveVideoActivity : BaseActivity() {
|
||||
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
|
||||
super.onQuitFullscreen(url, *objects)
|
||||
|
||||
orientationUtils?.backToProtVideo()
|
||||
}
|
||||
|
||||
})
|
||||
.build(binding.exoPlayContextId)
|
||||
hidePlayerControls()
|
||||
binding.exoPlayContextId.startPlayLogic()
|
||||
binding.exoPlayContextId.fullscreenButton.setOnClickListener {
|
||||
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()
|
||||
binding.exoPlayContextId.startWindowFullscreen(this, true, true)
|
||||
}
|
||||
|
||||
}
|
||||
private fun hidePlayerControls() {
|
||||
binding.exoPlayContextId.backButton.visibility = View.GONE
|
||||
@@ -237,6 +288,114 @@ class LiveVideoActivity : BaseActivity() {
|
||||
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) {
|
||||
@@ -264,14 +423,35 @@ class LiveVideoActivity : BaseActivity() {
|
||||
}
|
||||
alarmList.addAll(newData)
|
||||
alarmAdapter?.notifyDataSetChanged()
|
||||
updateAlarmListState()
|
||||
} catch (e: Exception) {
|
||||
Log.e("LiveVideoActivity", "请求告警列表失败", e)
|
||||
hasMore = false
|
||||
showAlarmListNetworkError()
|
||||
} finally {
|
||||
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() {
|
||||
super.onResume()
|
||||
binding.exoPlayContextId.onVideoResume()
|
||||
@@ -284,7 +464,21 @@ class LiveVideoActivity : BaseActivity() {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
ptzHandler.removeCallbacksAndMessages(null)
|
||||
orientationUtils?.releaseListener()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ class SettingsActivity : BaseActivity() {
|
||||
private var alertAudio: String? = null
|
||||
private var audioContent: String? = null
|
||||
private var isPasswordVerified = false
|
||||
private var verifiedPassword: String? = null
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
@@ -55,15 +56,15 @@ class SettingsActivity : BaseActivity() {
|
||||
if (isPasswordVerified) {
|
||||
val dialog = RecognitionModeDialog(this)
|
||||
dialog.setOnConfirmListener { mode ->
|
||||
updateRecognitionMode(mode)
|
||||
updateRecognitionMode(mode, verifiedPassword)
|
||||
}
|
||||
dialog.show()
|
||||
} else {
|
||||
showPasswordDialog {
|
||||
showPasswordDialog { inputPassword ->
|
||||
isPasswordVerified = true
|
||||
val dialog = RecognitionModeDialog(this)
|
||||
dialog.setOnConfirmListener { mode ->
|
||||
updateRecognitionMode(mode)
|
||||
updateRecognitionMode(mode, inputPassword)
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
@@ -94,22 +95,24 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
binding.itemWaterGunSetting.setOnClickListener {
|
||||
if (isPasswordVerified) {
|
||||
navigateToWaterGunSettings()
|
||||
navigateToWaterGunSettings(verifiedPassword)
|
||||
} else {
|
||||
showPasswordDialog {
|
||||
showPasswordDialog { inputPassword ->
|
||||
isPasswordVerified = true
|
||||
navigateToWaterGunSettings()
|
||||
navigateToWaterGunSettings(inputPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.itemAutoMode.setOnClickListener {
|
||||
if (isPasswordVerified) {
|
||||
val intent = Intent(this, AutoModeActivity::class.java)
|
||||
intent.putExtra("optionPassword", verifiedPassword)
|
||||
startActivity(intent)
|
||||
} else {
|
||||
showPasswordDialog {
|
||||
showPasswordDialog { inputPassword ->
|
||||
isPasswordVerified = true
|
||||
val intent = Intent(this, AutoModeActivity::class.java)
|
||||
intent.putExtra("optionPassword", inputPassword)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -258,7 +261,7 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRecognitionMode(mode: String) {
|
||||
private fun updateRecognitionMode(mode: String, optionPassword: String?) {
|
||||
val deviceId = DeviceIdEvent.getDeviceId()
|
||||
if (deviceId.isNullOrEmpty()) {
|
||||
Toast.makeText(this, "设备ID为空", Toast.LENGTH_SHORT).show()
|
||||
@@ -276,6 +279,7 @@ class SettingsActivity : BaseActivity() {
|
||||
val deviceBean = com.ckkj.defense_device.bean.DeviceBean(
|
||||
id = deviceId,
|
||||
patternPerception = patternPerception,
|
||||
optionPassword = optionPassword
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -328,7 +332,7 @@ class SettingsActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPasswordDialog(onVerified: () -> Unit) {
|
||||
private fun showPasswordDialog(onVerified: (String) -> Unit) {
|
||||
passwordDialog = PasswordInputDialog(this).apply {
|
||||
setOnConfirmListener { inputPassword ->
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
@@ -338,7 +342,8 @@ class SettingsActivity : BaseActivity() {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
dismiss()
|
||||
onVerified()
|
||||
verifiedPassword = inputPassword
|
||||
onVerified(inputPassword)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@SettingsActivity,
|
||||
@@ -365,8 +370,9 @@ class SettingsActivity : BaseActivity() {
|
||||
passwordDialog.show()
|
||||
}
|
||||
|
||||
private fun navigateToWaterGunSettings() {
|
||||
private fun navigateToWaterGunSettings(optionPassword: String? = null) {
|
||||
val intent = Intent(this, WaterGunSettingsActivity::class.java)
|
||||
intent.putExtra("optionPassword", optionPassword)
|
||||
val deviceId = DeviceIdEvent.getDeviceId()
|
||||
if (!deviceId.isNullOrEmpty()) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
|
||||
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
|
||||
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
|
||||
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
|
||||
import com.ckkj.defense_device.personal.PasswordInputDialog
|
||||
import com.ckkj.defense_device.utils.DeviceIdEvent
|
||||
@@ -26,6 +27,7 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
private var userId: String = ""
|
||||
private var isPasswordVerified: Boolean = false
|
||||
private lateinit var passwordDialog: PasswordInputDialog
|
||||
private var optionPassword: String = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -73,7 +75,7 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateUI(data: com.ckkj.defense_device.bean.ObtainInviterInformationBean) {
|
||||
binding.tvContactName.text = data.nickName ?: "未知用户"
|
||||
binding.tvContactName.text = data.nickName ?: ""
|
||||
binding.tvPhone.text = data.phonenumber ?: ""
|
||||
binding.tvIdCard.text = data.idCard ?: ""
|
||||
binding.tvMacAddress.text = data.mac ?: ""
|
||||
@@ -87,10 +89,12 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
binding.swUserStatus.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (!isPasswordVerified) {
|
||||
binding.swUserStatus.isChecked = !isChecked
|
||||
showPasswordDialog {
|
||||
showPasswordDialog(isChecked) {
|
||||
isPasswordVerified = true
|
||||
binding.swUserStatus.isChecked = isChecked
|
||||
}
|
||||
} else {
|
||||
updateFamilyUserStatus(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +103,7 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPasswordDialog(onVerified: () -> Unit) {
|
||||
private fun showPasswordDialog(status: Boolean, onVerified: () -> Unit) {
|
||||
passwordDialog = PasswordInputDialog(this).apply {
|
||||
setOnConfirmListener { inputPassword ->
|
||||
lifecycleScope.launch {
|
||||
@@ -109,6 +113,7 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
dismiss()
|
||||
updateFamilyUserStatus(status, inputPassword)
|
||||
onVerified()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
@@ -135,6 +140,48 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
|
||||
private fun updateFamilyUserStatus(status: Boolean) {
|
||||
updateFamilyUserStatus(status, optionPassword)
|
||||
}
|
||||
|
||||
private fun updateFamilyUserStatus(status: Boolean, password: String) {
|
||||
if (userId.isEmpty()) {
|
||||
Toast.makeText(this, "用户ID为空", Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (password.isNotEmpty()) {
|
||||
optionPassword = password
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
val request = UpdateFamilyUserRequest(
|
||||
id = userId,
|
||||
optionPassword = optionPassword,
|
||||
optionStatus = "status",
|
||||
status = if (status) "1" else "0"
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateFamilyUser(request)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
Toast.makeText(this@UserDetailsActivity, "状态修改成功", Toast.LENGTH_SHORT).show()
|
||||
DeviceUserUpdateEvent.notifyUpdate()
|
||||
} else {
|
||||
Toast.makeText(this@UserDetailsActivity, response.msg ?: "状态修改失败", Toast.LENGTH_SHORT).show()
|
||||
binding.swUserStatus.isChecked = !status
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@UserDetailsActivity, "网络请求失败", Toast.LENGTH_SHORT).show()
|
||||
binding.swUserStatus.isChecked = !status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDeleteDialog() {
|
||||
val dialog = CustomDialogFragmentText()
|
||||
dialog.setTitle(getString(R.string.tip))
|
||||
@@ -146,7 +193,7 @@ class UserDetailsActivity : AppCompatActivity() {
|
||||
if (isPasswordVerified) {
|
||||
deleteDeviceUser()
|
||||
} else {
|
||||
showPasswordDialog {
|
||||
showPasswordDialog(false) {
|
||||
isPasswordVerified = true
|
||||
deleteDeviceUser()
|
||||
}
|
||||
|
||||
@@ -79,29 +79,31 @@ class VoiceSettingsActivity : AppCompatActivity() {
|
||||
private fun updateDeviceAlertAudio() {
|
||||
deviceId?.let { id ->
|
||||
val newAlertAudio = if (binding.swWarningVoice.isChecked) "1" else "0"
|
||||
// 只有状态发生变化时才请求
|
||||
if (newAlertAudio != originalAlertAudio) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio)
|
||||
val response = apiService.updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200 && response.data != null) {
|
||||
Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
// 点击返回时总是触发网络请求
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val deviceBean = DeviceBean(id = id, alertAudio = newAlertAudio)
|
||||
val response = apiService.updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200 && response.data != null) {
|
||||
Toast.makeText(this@VoiceSettingsActivity, "修改成功", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
// 仅在状态实际发生变化时才显示错误提示
|
||||
if (newAlertAudio != originalAlertAudio) {
|
||||
Toast.makeText(this@VoiceSettingsActivity, response.msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
finish()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
finish()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
// 仅在状态实际发生变化时才显示失败提示
|
||||
if (newAlertAudio != originalAlertAudio) {
|
||||
Toast.makeText(this@VoiceSettingsActivity, "修改失败", Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
finish()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ class WaterGunSettingsActivity : BaseActivity() {
|
||||
private var currentStrikeRange: String = "7"
|
||||
private var currentStrikeInterval: String = "3"
|
||||
private var currentContainer: String = "容器4"
|
||||
private var optionPassword: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -41,6 +42,7 @@ class WaterGunSettingsActivity : BaseActivity() {
|
||||
currentStrikeRange = intent.getStringExtra("strikeRange") ?: "7"
|
||||
currentStrikeInterval = intent.getStringExtra("strikeInterval") ?: "3"
|
||||
currentContainer = intent.getStringExtra("container") ?: "容器4"
|
||||
optionPassword = intent.getStringExtra("optionPassword")
|
||||
|
||||
// 显示初始值
|
||||
binding.tvDistance.text = "${currentStrikeRange}m"
|
||||
@@ -100,7 +102,8 @@ class WaterGunSettingsActivity : BaseActivity() {
|
||||
try {
|
||||
val deviceBean = DeviceBean(
|
||||
id = deviceId,
|
||||
strikeRange = distance
|
||||
strikeRange = distance,
|
||||
optionPassword = optionPassword
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -131,7 +134,8 @@ class WaterGunSettingsActivity : BaseActivity() {
|
||||
try {
|
||||
val deviceBean = DeviceBean(
|
||||
id = deviceId,
|
||||
strikeInterval = interval
|
||||
strikeInterval = interval,
|
||||
optionPassword = optionPassword
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -162,7 +166,8 @@ class WaterGunSettingsActivity : BaseActivity() {
|
||||
try {
|
||||
val deviceBean = DeviceBean(
|
||||
id = deviceId,
|
||||
container = container
|
||||
container = container,
|
||||
optionPassword = optionPassword
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().updateDeviceInfo(deviceBean)
|
||||
withContext(Dispatchers.Main) {
|
||||
|
||||
@@ -202,14 +202,6 @@ class WiFiManagementActivity : AppCompatActivity() {
|
||||
}
|
||||
dialogBinding.rvWifiList.adapter = adapter
|
||||
|
||||
// 添加下拉刷新功能
|
||||
dialogBinding.swipeRefreshLayout.setOnRefreshListener {
|
||||
wifiManager.startScan()
|
||||
// 延迟1秒后停止刷新动画
|
||||
android.os.Handler().postDelayed({
|
||||
dialogBinding.swipeRefreshLayout.isRefreshing = false
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateBottomButtonVisibility() {
|
||||
@@ -229,9 +221,6 @@ class WiFiManagementActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateWifiList() {
|
||||
popupWindow?.contentView?.findViewById<androidx.swiperefreshlayout.widget.SwipeRefreshLayout>(R.id.swipeRefreshLayout)?.let { swipeRefreshLayout ->
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
popupWindow?.contentView?.findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.rvWifiList)?.let { recyclerView ->
|
||||
val wifiList = getWifiList()
|
||||
recyclerView.adapter = WifiListAdapter(wifiList) { wifiName ->
|
||||
@@ -253,4 +242,4 @@ class WiFiManagementActivity : AppCompatActivity() {
|
||||
unregisterReceiver(wifiScanReceiver)
|
||||
popupWindow?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.databinding.FaceVerificationActivityBinding
|
||||
import com.ckkj.defense_device.view.FaceAnalyzer
|
||||
import com.google.mlkit.vision.face.FaceDetection
|
||||
@@ -106,7 +107,7 @@ class FaceVerificationActivity : AppCompatActivity() {
|
||||
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
startCamera()
|
||||
} else {
|
||||
Toast.makeText(this, "需要相机权限才能进行人脸验证", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, R.string.need_camera_permission_for_face_verify, Toast.LENGTH_SHORT).show()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import bean.ForgotRequest
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.databinding.ForgetPasswordActivityBinding
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.google.firebase.crashlytics.buildtools.reloc.org.apache.http.util.TextUtils
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -20,6 +21,7 @@ import java.util.regex.Pattern
|
||||
|
||||
class ForgetPasswordActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ForgetPasswordActivityBinding
|
||||
private var isLoading = false
|
||||
private var countDownTimer: CountDownTimer? = null
|
||||
private var isSendingCode = false
|
||||
|
||||
@@ -55,15 +57,17 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
if (isPhoneNumberValid(phoneNumber)) {
|
||||
sendVerificationCode(phoneNumber)
|
||||
} else {
|
||||
Toast.makeText(this, "请输入有效的11位手机号", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.phone_tip), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, "请稍后再试", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.please_try_later), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
binding.btnNext.setOnClickListener {
|
||||
performResetPassword()
|
||||
if (!isLoading) {
|
||||
performResetPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
val response = RetrofitNetwork.getNetworkService().getCode(phoneNumber)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
Toast.makeText(this@ForgetPasswordActivity, "验证码已发送", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this@ForgetPasswordActivity, getString(R.string.code_sent_to), Toast.LENGTH_SHORT).show()
|
||||
startCountDown()
|
||||
} else {
|
||||
Toast.makeText(this@ForgetPasswordActivity, response.msg, Toast.LENGTH_SHORT).show()
|
||||
@@ -104,11 +108,10 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
* 开始验证码按钮倒计时
|
||||
*/
|
||||
private fun startCountDown() {
|
||||
binding.sendCodeBtn.isEnabled = false
|
||||
countDownTimer = object : CountDownTimer(COUNTDOWN_DURATION, COUNTDOWN_INTERVAL) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
val secondsRemaining = (millisUntilFinished / 1000).toInt()
|
||||
binding.sendCodeBtn.text = "${secondsRemaining}秒后重试"
|
||||
binding.sendCodeBtn.isEnabled = false
|
||||
binding.sendCodeBtn.text = getString(R.string.countdown_retry, millisUntilFinished / 1000)
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
@@ -123,7 +126,7 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
private fun resetSendButton() {
|
||||
countDownTimer?.cancel()
|
||||
countDownTimer = null
|
||||
binding.sendCodeBtn.text = "发送验证码"
|
||||
binding.sendCodeBtn.text = getString(R.string.resend_code)
|
||||
binding.sendCodeBtn.isEnabled = true
|
||||
}
|
||||
|
||||
@@ -137,34 +140,38 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
val confirmPassword = binding.etInviteCode.text.toString().trim()
|
||||
|
||||
if (!isPhoneNumberValid(phoneNumber)) {
|
||||
Toast.makeText(this, "手机号格式不正确", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.invalid_phone), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(smsCode)) {
|
||||
Toast.makeText(this, "请输入验证码", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.hint_code_zh), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(newPassword) || TextUtils.isEmpty(confirmPassword)) {
|
||||
Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.password_cannot_empty), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
if (newPassword.length < MIN_PASSWORD_LENGTH || newPassword.length > MAX_PASSWORD_LENGTH) {
|
||||
Toast.makeText(this, "密码长度需为${MIN_PASSWORD_LENGTH}-${MAX_PASSWORD_LENGTH}位", Toast.LENGTH_SHORT).show()
|
||||
val tip = getString(R.string.pwd_length_tip, MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH)
|
||||
Toast.makeText(this, tip, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
if (!newPassword.matches(Regex(".*[A-Za-z].*")) || !newPassword.matches(Regex(".*[0-9].*"))) {
|
||||
Toast.makeText(this, "密码需同时包含字母和数字", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.password_letter_digit), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
if (newPassword != confirmPassword) {
|
||||
Toast.makeText(this, "两次输入的密码不一致", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.password_mismatch), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
binding.btnNext.isEnabled = false
|
||||
binding.btnNext.text = "重置中..."
|
||||
val loadingDialog = LoadingDialog(this)
|
||||
.setMessage(getString(R.string.resetting))
|
||||
.apply { show() }
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
@@ -175,8 +182,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
)
|
||||
val response = RetrofitNetwork.getNetworkService().forgotPassword(forgotRequest)
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
binding.btnNext.isEnabled = true
|
||||
binding.btnNext.text = "确定"
|
||||
|
||||
if (response.code == 200) {
|
||||
Toast.makeText(this@ForgetPasswordActivity, R.string.reset_success, Toast.LENGTH_SHORT).show()
|
||||
@@ -187,8 +195,9 @@ class ForgetPasswordActivity : AppCompatActivity() {
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
binding.btnNext.isEnabled = true
|
||||
binding.btnNext.text = "确定"
|
||||
Toast.makeText(this@ForgetPasswordActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -14,6 +15,7 @@ import bean.LoginRequest
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.databinding.ActivityLoginBinding
|
||||
import com.ckkj.defense_device.home.HomeActivity
|
||||
import com.ckkj.defense_device.utils.ApiConfig
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.utils.UserinfoUtil
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
@@ -26,11 +28,13 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
private var currentLoginMode = LoginMode.SMS
|
||||
private var countDownTimer: CountDownTimer? = null
|
||||
|
||||
enum class LoginMode { SMS, PASSWORD }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ApiConfig.resetAuthRedirectFlag()
|
||||
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
@@ -73,7 +77,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
binding.registerText.visibility = View.VISIBLE
|
||||
binding.forgetText.visibility = View.GONE
|
||||
updateRegisterTextConstraints(center = false)
|
||||
updateRegisterTextConstraints(center = true)
|
||||
|
||||
//需要正确的方法名和参数
|
||||
updateLoginButtonTopConstraint(R.id.smsRowLayout)
|
||||
@@ -98,7 +102,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
private fun updateLoginButtonTopConstraint(topTargetId: Int) {
|
||||
val params = binding.loginBtn.layoutParams as ConstraintLayout.LayoutParams
|
||||
params.topToBottom = topTargetId
|
||||
params.topMargin = 32
|
||||
binding.loginBtn.layoutParams = params
|
||||
}
|
||||
|
||||
@@ -115,7 +118,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
params.endToEnd = ConstraintSet.UNSET
|
||||
}
|
||||
params.topToBottom = binding.loginBtn.id
|
||||
params.topMargin = 20
|
||||
binding.registerText.layoutParams = params
|
||||
}
|
||||
|
||||
@@ -166,25 +168,24 @@ class LoginActivity : AppCompatActivity() {
|
||||
binding.registerText.setOnClickListener {
|
||||
val intent= Intent(this, RegisterActivity::class.java)
|
||||
startActivity(intent)
|
||||
Toast.makeText(this, R.string.redirect_register_page, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
binding.forgetText.setOnClickListener {
|
||||
val intent= Intent(this, ForgetPasswordActivity::class.java)
|
||||
startActivity(intent)
|
||||
Toast.makeText(this, R.string.forgot_password_page, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startCountDown() {
|
||||
binding.sendCodeBtn.isEnabled = false
|
||||
object : CountDownTimer(60000, 1000) {
|
||||
countDownTimer = object : CountDownTimer(60000, 1000) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
binding.sendCodeBtn.text = getString(R.string.countdown_retry, millisUntilFinished / 1000)
|
||||
}
|
||||
override fun onFinish() {
|
||||
binding.sendCodeBtn.text = getString(R.string.send_code)
|
||||
binding.sendCodeBtn.text = getString(R.string.resend_code)
|
||||
binding.sendCodeBtn.isEnabled = true
|
||||
countDownTimer = null
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
@@ -218,9 +219,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
phonenumber = phone,
|
||||
smsCode = smsCode
|
||||
)
|
||||
android.util.Log.d("LoginActivity", "短信登录请求: $loginRequest")
|
||||
val response = RetrofitNetwork.getNetworkService().login(loginRequest)
|
||||
android.util.Log.d("LoginActivity", "短信登录响应: code=${response.code}, msg=${response.msg}")
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200 && response.data != null) {
|
||||
UserinfoUtil.saveUserInfo(response.data)
|
||||
@@ -234,7 +233,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("LoginActivity", "短信登录异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@LoginActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -251,14 +249,13 @@ class LoginActivity : AppCompatActivity() {
|
||||
phonenumber = phone,
|
||||
password = password
|
||||
)
|
||||
android.util.Log.d("LoginActivity", "密码登录请求: $loginRequest")
|
||||
val response = RetrofitNetwork.getNetworkService().login(loginRequest)
|
||||
android.util.Log.d("LoginActivity", "密码登录响应: code=${response.code}, msg=${response.msg}")
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200 && response.data != null) {
|
||||
UserinfoUtil.saveUserInfo(response.data)
|
||||
Toast.makeText(this@LoginActivity, R.string.password_login_success_title, Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(this@LoginActivity, HomeActivity::class.java)
|
||||
//清除任务栈
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
finish()
|
||||
@@ -267,7 +264,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("LoginActivity", "密码登录异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@LoginActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@@ -285,4 +281,10 @@ class LoginActivity : AppCompatActivity() {
|
||||
gradientDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT
|
||||
view.background = gradientDrawable
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
countDownTimer?.cancel()
|
||||
countDownTimer = null
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import bean.RegisterRequest
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.databinding.RegisterActivityBinding
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -80,7 +81,7 @@ class RegisterActivity : AppCompatActivity() {
|
||||
val response = RetrofitNetwork.getNetworkService().getCode(userName)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (response.code == 200) {
|
||||
Toast.makeText(this@RegisterActivity, "验证码已发送", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this@RegisterActivity, R.string.code_already_sent, Toast.LENGTH_SHORT).show()
|
||||
startCountDown()
|
||||
} else {
|
||||
Toast.makeText(this@RegisterActivity, response.msg, Toast.LENGTH_SHORT).show()
|
||||
@@ -99,11 +100,10 @@ class RegisterActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun startCountDown() {
|
||||
binding.sendCodeBtn.isEnabled = false
|
||||
countDownTimer = object : CountDownTimer(COUNTDOWN_DURATION, COUNTDOWN_INTERVAL) {
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
val secondsRemaining = (millisUntilFinished / 1000).toInt()
|
||||
binding.sendCodeBtn.text = "${secondsRemaining}秒后重试"
|
||||
binding.sendCodeBtn.isEnabled = false
|
||||
binding.sendCodeBtn.text = getString(R.string.countdown_retry, millisUntilFinished / 1000)
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
@@ -115,7 +115,7 @@ class RegisterActivity : AppCompatActivity() {
|
||||
private fun resetSendButton() {
|
||||
countDownTimer?.cancel()
|
||||
countDownTimer = null
|
||||
binding.sendCodeBtn.text = getString(R.string.send_code)
|
||||
binding.sendCodeBtn.text = getString(R.string.resend_code)
|
||||
binding.sendCodeBtn.isEnabled = true
|
||||
isSendingCode = false
|
||||
}
|
||||
@@ -158,7 +158,9 @@ class RegisterActivity : AppCompatActivity() {
|
||||
|
||||
isLoading = true
|
||||
binding.btnNext.isEnabled = false
|
||||
binding.btnNext.text = getString(R.string.registering)
|
||||
val loadingDialog = LoadingDialog(this)
|
||||
.setMessage(getString(R.string.registering))
|
||||
.apply { show() }
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
@@ -174,12 +176,13 @@ class RegisterActivity : AppCompatActivity() {
|
||||
val response = RetrofitNetwork.getNetworkService().register(registerRequest)
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
binding.btnNext.isEnabled = true
|
||||
binding.btnNext.text = getString(R.string.next_step)
|
||||
|
||||
if (response.code == 200) {
|
||||
Toast.makeText(this@RegisterActivity, R.string.register_success, Toast.LENGTH_SHORT).show()
|
||||
val intent = Intent(this@RegisterActivity, LoginActivity::class.java)
|
||||
val intent = Intent(this@RegisterActivity, RegisterSuccessActivity::class.java)
|
||||
//清除任务栈
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
startActivity(intent)
|
||||
finish()
|
||||
@@ -190,8 +193,8 @@ class RegisterActivity : AppCompatActivity() {
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
binding.btnNext.isEnabled = true
|
||||
binding.btnNext.text = getString(R.string.next_step)
|
||||
Toast.makeText(this@RegisterActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,45 @@
|
||||
package com.ckkj.defense_device.personal
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.bean.RecommendationCodeBean
|
||||
import com.ckkj.defense_device.bean.SelectInviterInfoBean
|
||||
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
|
||||
import com.ckkj.defense_device.databinding.DialogExpireTimeBinding
|
||||
import com.ckkj.defense_device.databinding.InvitationCodeActivityBinding
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.google.gson.Gson
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import com.maning.mlkitscanner.scan.MNScanManager
|
||||
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.Hashtable
|
||||
@@ -44,7 +57,24 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
private var isLoading = false // 是否正在加载
|
||||
private var hasGeneratedQrCode = false // 是否已经生成过二维码
|
||||
|
||||
private val requestPermissionsLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
|
||||
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
|
||||
} else {
|
||||
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
|
||||
}
|
||||
if (cameraGranted && storageGranted) {
|
||||
startScan()
|
||||
} else {
|
||||
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "InvitationCodeActivity"
|
||||
const val EXPIRE_24H = 24 * 60 * 60 * 1000L
|
||||
const val EXPIRE_7D = 7 * 24 * 60 * 60 * 1000L
|
||||
}
|
||||
@@ -90,15 +120,136 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
|
||||
// 撤销邀请按钮
|
||||
binding.btnCancelInvite.setOnClickListener {
|
||||
showPasswordDialog {
|
||||
cancelInvitation()
|
||||
if (generatedInviteCode.isEmpty()) {
|
||||
ToastUtils.showError(this, getString(R.string.qr_code_expired))
|
||||
return@setOnClickListener
|
||||
}
|
||||
showPasswordDialog { password, dialog ->
|
||||
cancelInvitation(password, dialog)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成/重新生成按钮
|
||||
binding.btnRegenerate.setOnClickListener {
|
||||
showPasswordDialog {
|
||||
generateOrRegenerateInvitation()
|
||||
showPasswordDialog { password, dialog ->
|
||||
generateOrRegenerateInvitation(password, dialog)
|
||||
}
|
||||
}
|
||||
binding.sys.setOnClickListener {
|
||||
checkPermissionsAndScan()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPermissionsAndScan() {
|
||||
val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
|
||||
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_IMAGES)
|
||||
} else {
|
||||
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
|
||||
storagePermission == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
startScan()
|
||||
} else {
|
||||
val permissionsToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arrayOf(
|
||||
Manifest.permission.CAMERA,
|
||||
Manifest.permission.READ_MEDIA_IMAGES
|
||||
)
|
||||
} else {
|
||||
arrayOf(
|
||||
Manifest.permission.CAMERA,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
}
|
||||
requestPermissionsLauncher.launch(permissionsToRequest)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startScan() {
|
||||
MNScanManager.startScan(this, object : MNScanCallback {
|
||||
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
||||
when (resultCode) {
|
||||
MNScanManager.RESULT_SUCCESS -> {
|
||||
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
|
||||
if (!result.isNullOrEmpty()) {
|
||||
val inviteCode = parseInviteCode(result[0])
|
||||
if (inviteCode.isNullOrEmpty()) {
|
||||
Toast.makeText(this@InvitationCodeActivity, "无效的邀请码", Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
fetchInviterInfo(inviteCode)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this@InvitationCodeActivity, "扫码结果为空", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
MNScanManager.RESULT_FAIL -> {
|
||||
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
|
||||
Toast.makeText(this@InvitationCodeActivity, "扫码失败: $error", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
MNScanManager.RESULT_CANCLE -> {
|
||||
Toast.makeText(this@InvitationCodeActivity, "扫码取消", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun parseInviteCode(scanResult: String): String? {
|
||||
val trimmed = scanResult.trim()
|
||||
if (trimmed.isEmpty()) return null
|
||||
return try {
|
||||
if (trimmed.startsWith("{")) {
|
||||
JSONObject(trimmed).optString("code").takeIf { it.isNotEmpty() }
|
||||
} else {
|
||||
trimmed
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
trimmed
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchInviterInfo(inviteCode: String) {
|
||||
val loadingDialog = LoadingDialog(this).apply { show() }
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService()
|
||||
.selectInviterInfo(SelectInviterInfoBean(code = inviteCode))
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
if (response.code == 200 && response.data != null) {
|
||||
val inviter = response.data
|
||||
val parentId = inviter.resolveParentId()
|
||||
if (parentId.isNullOrEmpty()) {
|
||||
Toast.makeText(
|
||||
this@InvitationCodeActivity,
|
||||
getString(R.string.network_error),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@withContext
|
||||
}
|
||||
val intent = Intent(this@InvitationCodeActivity, JoinFamilyInviteActivity::class.java).apply {
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_INVITE_CODE, inviteCode)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_NICK_NAME, inviter.nickName)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_AVATAR, inviter.avatar)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_PARENT_ID, parentId)
|
||||
putExtra(JoinFamilyInviteActivity.EXTRA_USER_ID, inviter.userId)
|
||||
}
|
||||
startActivity(intent)
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this@InvitationCodeActivity,
|
||||
response.msg.ifBlank { getString(R.string.network_error) },
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
loadingDialog.dismiss()
|
||||
Toast.makeText(this@InvitationCodeActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,14 +257,56 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
/**
|
||||
* 生成或重新生成邀请码
|
||||
*/
|
||||
private fun generateOrRegenerateInvitation() {
|
||||
// 根据当前选择的时效生成邀请码
|
||||
generateQRCode()
|
||||
showQrCode()
|
||||
// 更新按钮文字为"重新生成"
|
||||
binding.btnRegenerate.text = getString(R.string.regenerate)
|
||||
hasGeneratedQrCode = true
|
||||
ToastUtils.showSuccess(this, getString(R.string.operation_success))
|
||||
private fun generateOrRegenerateInvitation(password: String, passwordDialog: PasswordInputDialog) {
|
||||
if (isLoading) return
|
||||
isLoading = true
|
||||
val loadingDialog = LoadingDialog(this).apply { show() }
|
||||
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService()
|
||||
.generateRecommendationCode(VerifyOptionPasswordRequest(password))
|
||||
Log.d(TAG, "生成推荐码响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
if (response.code == 200) {
|
||||
val parsed = parseRecommendationCode(response.data)
|
||||
if (parsed != null) {
|
||||
passwordDialog.dismiss()
|
||||
generatedInviteCode = parsed.first
|
||||
expireTimestamp = parsed.second
|
||||
qrCodeContent =
|
||||
"{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}"
|
||||
Log.d(
|
||||
TAG,
|
||||
"推荐码生成成功: Code=$generatedInviteCode, expireTimestamp=$expireTimestamp, qrContent=$qrCodeContent"
|
||||
)
|
||||
generateQRCode()
|
||||
showQrCode()
|
||||
binding.btnRegenerate.text = getString(R.string.regenerate)
|
||||
hasGeneratedQrCode = true
|
||||
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
|
||||
} else {
|
||||
Log.w(TAG, "推荐码解析失败, raw data=${response.data}")
|
||||
passwordDialog.showError(getString(R.string.network_error))
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "生成推荐码失败: code=${response.code}, msg=${response.msg}")
|
||||
passwordDialog.showError(
|
||||
response.msg.ifEmpty { getString(R.string.password_error) }
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "生成推荐码异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
passwordDialog.showError(getString(R.string.network_error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showExpireTimeDialog() {
|
||||
@@ -151,9 +344,6 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
binding.tvExpireTime.text = getString(R.string.expire_7d)
|
||||
}
|
||||
}
|
||||
// 显示二维码并生成
|
||||
showQrCode()
|
||||
generateQRCode()
|
||||
expireTimeDialog?.dismiss()
|
||||
}
|
||||
|
||||
@@ -173,22 +363,28 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun generateQRCode() {
|
||||
// 检查二维码是否过期,如果过期则生成新的邀请码
|
||||
val isExpired = expireTimestamp > 0 && System.currentTimeMillis() > expireTimestamp
|
||||
val content = if (qrCodeContent.isNotEmpty() && !isExpired) qrCodeContent else generateInviteCode()
|
||||
val qrBitmap = createQRCodeWithLogo(content, 400, 400)
|
||||
if (qrCodeContent.isEmpty()) return
|
||||
val qrBitmap = createQRCodeWithLogo(qrCodeContent, 400, 400)
|
||||
binding.ivQrCode.setImageBitmap(qrBitmap)
|
||||
}
|
||||
|
||||
private fun generateInviteCode(): String {
|
||||
val timestamp = System.currentTimeMillis()
|
||||
expireTimestamp = timestamp + expireTimeInMillis
|
||||
generatedInviteCode = "INV${timestamp}"
|
||||
qrCodeContent = "{\"type\":\"invite\",\"code\":\"$generatedInviteCode\",\"expire\":$expireTimestamp}"
|
||||
// 输出日志,方便调试验证
|
||||
android.util.Log.d("InvitationCode", "生成的二维码内容: $qrCodeContent")
|
||||
android.util.Log.d("InvitationCode", "推荐码: $generatedInviteCode, 过期时间戳: $expireTimestamp")
|
||||
return qrCodeContent
|
||||
private fun parseRecommendationCode(data: Any?): Pair<String, Long>? {
|
||||
if (data == null) return null
|
||||
return when (data) {
|
||||
is String -> {
|
||||
if (data.isEmpty()) null
|
||||
else Pair(data, System.currentTimeMillis() + expireTimeInMillis)
|
||||
}
|
||||
else -> {
|
||||
val bean = Gson().fromJson(Gson().toJson(data), RecommendationCodeBean::class.java)
|
||||
val code = bean.inviteCode?.takeIf { it.isNotEmpty() }
|
||||
?: bean.code?.takeIf { it.isNotEmpty() }
|
||||
?: return null
|
||||
val expire = bean.expireTime ?: bean.expire
|
||||
?: (System.currentTimeMillis() + expireTimeInMillis)
|
||||
Pair(code, expire)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* 这段代码生成带Logo的二维码:
|
||||
@@ -415,56 +611,62 @@ class InvitationCodeActivity : AppCompatActivity() {
|
||||
/**
|
||||
* 撤销邀请
|
||||
*/
|
||||
private fun cancelInvitation() {
|
||||
// 撤销邀请逻辑
|
||||
hideQrCode()
|
||||
generatedInviteCode = ""
|
||||
qrCodeContent = ""
|
||||
expireTimestamp = 0
|
||||
hasGeneratedQrCode = false
|
||||
// 恢复按钮文字为"生成邀请码"
|
||||
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
|
||||
// 显示提示
|
||||
ToastUtils.showSuccess(this, getString(R.string.operation_success))
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示密码输入对话框
|
||||
* @param onSuccess 验证成功后的回调
|
||||
*/
|
||||
private fun showPasswordDialog(onSuccess: () -> Unit) {
|
||||
val dialog = PasswordInputDialog(this)
|
||||
dialog.setTitle(getString(R.string.input_op_pwd))
|
||||
dialog.setOnConfirmListener { password ->
|
||||
verifyPassword(password, dialog, onSuccess)
|
||||
private fun cancelInvitation(password: String, passwordDialog: PasswordInputDialog) {
|
||||
if (isLoading) return
|
||||
if (generatedInviteCode.isEmpty()) {
|
||||
passwordDialog.showError(getString(R.string.qr_code_expired))
|
||||
return
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
isLoading = true
|
||||
val loadingDialog = LoadingDialog(this).apply { show() }
|
||||
|
||||
/**
|
||||
* 验证操作密码
|
||||
*/
|
||||
private fun verifyPassword(password: String, dialog: PasswordInputDialog, onSuccess: () -> Unit) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService()
|
||||
.verifyOptionPassword(VerifyOptionPasswordRequest(password))
|
||||
.revokeInvitation(VerifyOptionPasswordRequest(password))
|
||||
Log.d(TAG, "撤销邀请响应: code=${response.code}, msg=${response.msg}")
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
if (response.code == 200) {
|
||||
dialog.dismiss()
|
||||
onSuccess.invoke()
|
||||
passwordDialog.dismiss()
|
||||
hideQrCode()
|
||||
generatedInviteCode = ""
|
||||
qrCodeContent = ""
|
||||
expireTimestamp = 0
|
||||
hasGeneratedQrCode = false
|
||||
binding.btnRegenerate.text = getString(R.string.generate_invite_code)
|
||||
ToastUtils.showSuccess(this@InvitationCodeActivity, getString(R.string.operation_success))
|
||||
} else {
|
||||
dialog.showError(getString(R.string.password_error))
|
||||
passwordDialog.showError(
|
||||
response.msg.ifEmpty { getString(R.string.password_error) }
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "撤销邀请异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
dialog.showError(getString(R.string.network_error))
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
passwordDialog.showError(getString(R.string.network_error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示密码输入对话框
|
||||
*/
|
||||
private fun showPasswordDialog(onConfirm: (String, PasswordInputDialog) -> Unit) {
|
||||
val dialog = PasswordInputDialog(this)
|
||||
dialog.setTitle(getString(R.string.input_op_pwd))
|
||||
dialog.setOnConfirmListener { password ->
|
||||
onConfirm(password, dialog)
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
expireTimeDialog?.dismiss()
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.ckkj.defense_device.personal
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.ckkj.defense_device.R
|
||||
import com.ckkj.defense_device.bean.JoinTheFamilyBean
|
||||
import com.ckkj.defense_device.databinding.JoinFamilyInviteActivityBinding
|
||||
import com.ckkj.defense_device.home.HomeActivity
|
||||
import com.ckkj.defense_device.utils.RetrofitNetwork
|
||||
import com.ckkj.defense_device.view.LoadingDialog
|
||||
import com.gyf.immersionbar.ImmersionBar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// 邀请加入家庭
|
||||
class JoinFamilyInviteActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: JoinFamilyInviteActivityBinding
|
||||
private var isLoading = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = JoinFamilyInviteActivityBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
ImmersionBar.with(this)
|
||||
.statusBarDarkFont(true)
|
||||
.titleBar(binding.topBar)
|
||||
.init()
|
||||
|
||||
binding.ivBack.setOnClickListener { finish() }
|
||||
binding.btnJoinNow.setOnClickListener { joinFamily() }
|
||||
displayInviterInfo()
|
||||
}
|
||||
|
||||
private fun joinFamily() {
|
||||
if (isLoading) return
|
||||
|
||||
val parentId = intent.getStringExtra(EXTRA_USER_ID).orEmpty()
|
||||
val inviteCode = intent.getStringExtra(EXTRA_INVITE_CODE).orEmpty()
|
||||
if (parentId.isEmpty() || inviteCode.isEmpty()) {
|
||||
Log.e(TAG, "加入家庭参数无效: parentId=$parentId, inviteCode=$inviteCode")
|
||||
Toast.makeText(this, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
Log.e(TAG, "加入家庭请求: parentId=$parentId, inviteCode=$inviteCode")
|
||||
isLoading = true
|
||||
val loadingDialog = LoadingDialog(this).apply { show() }
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val response = RetrofitNetwork.getNetworkService()
|
||||
.joinTheFamily(JoinTheFamilyBean(parentId = parentId, inviteCode = inviteCode))
|
||||
Log.e(TAG, "加入家庭响应: code=${response.code}, msg=${response.msg}, data=${response.data}")
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
if (response.code == 200) {
|
||||
Log.e(TAG, "加入家庭成功,返回首页刷新")
|
||||
ToastUtils.showSuccess(this@JoinFamilyInviteActivity, getString(R.string.operation_success))
|
||||
val homeIntent = Intent(this@JoinFamilyInviteActivity, HomeActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
putExtra(HomeActivity.EXTRA_REFRESH, true)
|
||||
}
|
||||
startActivity(homeIntent)
|
||||
finish()
|
||||
} else {
|
||||
Log.e(TAG, "加入家庭失败: code=${response.code}, msg=${response.msg}")
|
||||
Toast.makeText(
|
||||
this@JoinFamilyInviteActivity,
|
||||
response.msg.ifBlank { getString(R.string.network_error) },
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "加入家庭请求异常", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
isLoading = false
|
||||
loadingDialog.dismiss()
|
||||
Toast.makeText(this@JoinFamilyInviteActivity, R.string.network_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayInviterInfo() {
|
||||
val nickName = intent.getStringExtra(EXTRA_NICK_NAME).orEmpty()
|
||||
val avatar = intent.getStringExtra(EXTRA_AVATAR)
|
||||
|
||||
binding.tvNickname.text = nickName.ifEmpty { getString(R.string.nickname_placeholder) }
|
||||
binding.tvInviteMessage.text = getString(
|
||||
R.string.invite_join_family_message,
|
||||
nickName.ifEmpty { getString(R.string.nickname_placeholder) }
|
||||
)
|
||||
|
||||
val requestOptions = RequestOptions()
|
||||
.placeholder(R.mipmap.img)
|
||||
.error(R.mipmap.img)
|
||||
|
||||
Glide.with(this)
|
||||
.load(avatar)
|
||||
.apply(requestOptions)
|
||||
.circleCrop()
|
||||
.into(binding.ivAvatar)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "JoinFamilyInviteActivity"
|
||||
const val EXTRA_INVITE_CODE = "invite_code"
|
||||
const val EXTRA_NICK_NAME = "nick_name"
|
||||
const val EXTRA_AVATAR = "avatar"
|
||||
const val EXTRA_PARENT_ID = "parent_id"
|
||||
const val EXTRA_USER_ID = "user_id"
|
||||
}
|
||||
}
|
||||
@@ -163,7 +163,7 @@ class RetrievePasswordActivity : AppCompatActivity() {
|
||||
override fun onFinish() {
|
||||
isCountingDown = false
|
||||
binding.btnGetCode.isEnabled = true
|
||||
binding.btnGetCode.text = getString(R.string.get_verification_code)
|
||||
binding.btnGetCode.text = getString(R.string.resend_code)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
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
|
||||
import java.io.File
|
||||
|
||||
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
|
||||
// 显示缓存大小
|
||||
displayCacheSize()
|
||||
|
||||
setupClickListeners()
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
// 返回按钮
|
||||
binding.ivBack.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
|
||||
// 检查更新
|
||||
binding.itemCheckUpdate.setOnClickListener {
|
||||
Toast.makeText(this, "正在检查更新...", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
binding.itemClearCache.setOnClickListener {
|
||||
clearCache()
|
||||
displayCacheSize() // 更新缓存大小显示
|
||||
Toast.makeText(this, "缓存已清除", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
// 用户协议
|
||||
binding.itemUserAgreement.setOnClickListener {
|
||||
|
||||
}
|
||||
|
||||
// 隐私政策
|
||||
binding.itemPrivacyPolicy.setOnClickListener {
|
||||
|
||||
}
|
||||
|
||||
// 注销账号
|
||||
binding.itemLogout.setOnClickListener {
|
||||
val intent = Intent(this, CancelAccountActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
//缓存方法
|
||||
private fun displayCacheSize() {
|
||||
val cacheSize = getCacheSize()
|
||||
val sizeString = when {
|
||||
cacheSize >= 1024 * 1024 -> String.format("%.2f MB", cacheSize / (1024.0 * 1024.0))
|
||||
else -> String.format("%d KB", cacheSize / 1024)
|
||||
}
|
||||
binding.tvCacheSize.text = sizeString
|
||||
}
|
||||
|
||||
private fun getCacheSize(): Long {
|
||||
val cacheDir = cacheDir
|
||||
return getFolderSize(cacheDir)
|
||||
}
|
||||
|
||||
private fun getFolderSize(dir: File?): Long {
|
||||
var size = 0L
|
||||
if (dir != null && dir.isDirectory) {
|
||||
dir.listFiles()?.forEach { file ->
|
||||
size += if (file.isFile) {
|
||||
file.length()
|
||||
} else {
|
||||
getFolderSize(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
private fun clearCache() {
|
||||
val cacheDir = cacheDir
|
||||
deleteDir(cacheDir)
|
||||
}
|
||||
|
||||
private fun deleteDir(dir: File?): Boolean {
|
||||
if (dir != null && dir.isDirectory) {
|
||||
dir.listFiles()?.forEach { file ->
|
||||
deleteDir(file)
|
||||
}
|
||||
}
|
||||
return dir?.delete() ?: false
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
package com.ckkj.defense_device.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.blankj.utilcode.util.Utils
|
||||
import com.ckkj.defense_device.bean.BaseInfo
|
||||
import com.ckkj.defense_device.login.LoginActivity
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonParser
|
||||
import okhttp3.Interceptor
|
||||
@@ -24,10 +30,17 @@ object ApiConfig {
|
||||
@Volatile
|
||||
public var authToken: String = ""
|
||||
|
||||
@Volatile
|
||||
private var isRedirectingToLogin = false
|
||||
|
||||
fun updateAuthToken(token: String) {
|
||||
authToken = token
|
||||
}
|
||||
|
||||
fun resetAuthRedirectFlag() {
|
||||
isRedirectingToLogin = false
|
||||
}
|
||||
|
||||
fun hasAuthToken(): Boolean {
|
||||
return authToken.isNotBlank()
|
||||
}
|
||||
@@ -116,8 +129,8 @@ object ApiConfig {
|
||||
val businessCode = if (jsonObject.has("code")) jsonObject.get("code").asInt else 0
|
||||
|
||||
if (httpCode == 401 || httpCode == 424 || businessCode == 401) {
|
||||
clearUserLoginInfo()
|
||||
val msg = if (jsonObject.has("msg")) jsonObject.get("msg").asString else "登录已过期,请重新登录"
|
||||
redirectToLogin(msg)
|
||||
return@Interceptor buildFakeErrorResponse(request, msg, code = if (businessCode != 0) businessCode else httpCode)
|
||||
}
|
||||
|
||||
@@ -138,7 +151,7 @@ object ApiConfig {
|
||||
*/
|
||||
private fun buildFakeErrorResponse(request: Request, msg: String, code: Int = 1): Response {
|
||||
val baseInfo = BaseInfo(
|
||||
message = msg,
|
||||
msg = msg,
|
||||
code = code,
|
||||
data = null,
|
||||
)
|
||||
@@ -179,12 +192,36 @@ object ApiConfig {
|
||||
private fun buildAuthorizationValue(token: String): String {
|
||||
return if (token.startsWith("Bearer ")) token else "Bearer $token"
|
||||
}
|
||||
//401跳转登录界面
|
||||
private fun redirectToLogin(msg: String) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
if (isRedirectingToLogin) return@post
|
||||
val current = ActivityManager.getCurrentActivity()
|
||||
if (current is LoginActivity) return@post
|
||||
|
||||
isRedirectingToLogin = true
|
||||
clearUserLoginInfo()
|
||||
|
||||
val context = current ?: Utils.getApp()
|
||||
ActivityManager.finishAllActivities()
|
||||
|
||||
val intent = Intent(context, LoginActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
context.startActivity(intent)
|
||||
|
||||
if (msg.isNotBlank()) {
|
||||
Toast.makeText(Utils.getApp(), msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearUserLoginInfo() {
|
||||
try {
|
||||
authToken = ""
|
||||
UserinfoUtil.clearUserInfo()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
authToken = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,16 +14,22 @@ import com.ckkj.defense_device.bean.DeleteBean
|
||||
import com.ckkj.defense_device.bean.DeleteDeviceUserRequest
|
||||
import com.ckkj.defense_device.bean.DeviceAlarmResponse
|
||||
import com.ckkj.defense_device.bean.FamilyMembersBean
|
||||
import com.ckkj.defense_device.bean.InviterInfoBean
|
||||
import com.ckkj.defense_device.bean.JoinTheFamilyBean
|
||||
import com.ckkj.defense_device.bean.MessageBean
|
||||
import com.ckkj.defense_device.bean.MessageListResponse
|
||||
import com.ckkj.defense_device.bean.ObtainInviterInformationBean
|
||||
import com.ckkj.defense_device.bean.ObtainInviterInformationRequest
|
||||
import com.ckkj.defense_device.bean.SelectInviterInfoBean
|
||||
import com.ckkj.defense_device.bean.SwitchFamilyRequest
|
||||
import com.ckkj.defense_device.bean.UpdateFamilyUserRequest
|
||||
import com.ckkj.defense_device.bean.UpdatePassword
|
||||
import com.ckkj.defense_device.bean.UpdateUserInfo
|
||||
import com.ckkj.defense_device.bean.UploadResponse
|
||||
import com.ckkj.defense_device.bean.UserInfoBean
|
||||
import com.ckkj.defense_device.bean.VerifyOptionPasswordRequest
|
||||
import com.ckkj.defense_device.bean.VideoStreamInfo
|
||||
import com.ckkj.defense_device.bean.VideoStreamRequest
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
@@ -33,6 +39,25 @@ import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Query
|
||||
|
||||
// ┏┓ ┏┓
|
||||
//┏┛┻━━━┛┻┓
|
||||
//┃ ┃
|
||||
//┃ ━ ┃
|
||||
//┃ ┳┛ ┗┳ ┃
|
||||
//┃ ┃
|
||||
//┃ ┻ ┃
|
||||
//┃ ┃
|
||||
//┗━┓ ┏━┛
|
||||
// ┃ ┃
|
||||
// ┃ ┃
|
||||
// ┃ ┗━━━┓
|
||||
// ┃ ┣┓
|
||||
// ┃ ┏┛
|
||||
// ┗┓┓┏━┳┓┏┛
|
||||
// ┃┫┫ ┃┫┫
|
||||
// ┗┻┛ ┗┻┛
|
||||
//神兽护体,代码零Bug,需求不改,一次编译通过
|
||||
|
||||
interface ApiService {
|
||||
/**
|
||||
* 获取登录验证码
|
||||
@@ -191,7 +216,7 @@ interface ApiService {
|
||||
|
||||
|
||||
/**
|
||||
* 获取邀请人信息
|
||||
* 获取家庭成员信息
|
||||
*/
|
||||
@POST("/app/obtainInviterInformation")
|
||||
suspend fun obtainInviterInformation(@Body request: ObtainInviterInformationRequest): GetCodeBean<ObtainInviterInformationBean>
|
||||
@@ -250,7 +275,95 @@ interface ApiService {
|
||||
/**
|
||||
* 修改家庭用户状态
|
||||
*/
|
||||
//@POST("/app/updateFamilyUser")
|
||||
@POST("/app/updateFamilyUser")
|
||||
suspend fun updateFamilyUser(@Body request: UpdateFamilyUserRequest): GetCodeBean<Any>
|
||||
|
||||
|
||||
/**
|
||||
* 获取邀请人信息
|
||||
*/
|
||||
@POST("/app/selectInviterInfo")
|
||||
suspend fun selectInviterInfo(@Body request: SelectInviterInfoBean): GetCodeBean<InviterInfoBean>
|
||||
|
||||
|
||||
/**
|
||||
* 加入xxx家庭
|
||||
*/
|
||||
@POST("/app/joinTheFamily")
|
||||
suspend fun joinTheFamily(@Body request: JoinTheFamilyBean): GetCodeBean<Any>
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ckkj.defense_device.utils
|
||||
|
||||
import com.ckkj.defense_device.utils.crypto.CryptoInterceptor
|
||||
import com.ckkj.defense_device.utils.crypto.CryptoKeyManager
|
||||
import com.google.gson.Gson
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
@@ -12,7 +14,8 @@ import java.util.concurrent.TimeUnit
|
||||
object RetrofitNetwork {
|
||||
|
||||
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"
|
||||
|
||||
private var retrofit: Retrofit? = null
|
||||
private val gson = Gson()
|
||||
@@ -64,12 +67,39 @@ object RetrofitNetwork {
|
||||
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
|
||||
.addInterceptor(ApiConfig.createHeadersInterceptor())
|
||||
.addInterceptor(ApiConfig.getUnifiedInterceptor()) // 使用 ApiConfig 中的统一拦截器
|
||||
.addInterceptor(ApiConfig.getUnifiedInterceptor())
|
||||
|
||||
// 加密拦截器(密钥就绪即启用,按开关自动判断)
|
||||
if (CryptoKeyManager.isCryptoEnabled && CryptoKeyManager.isKeyConfigured()) {
|
||||
builder.addInterceptor(getCryptoInterceptor())
|
||||
}
|
||||
|
||||
if (enableLogging) {
|
||||
builder.addInterceptor(ApiConfig.getLoggingInterceptor()) // 使用 ApiConfig 中的日志拦截器
|
||||
builder.addInterceptor(ApiConfig.getLoggingInterceptor())
|
||||
}
|
||||
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取加密拦截器实例
|
||||
* 每次都重新创建,确保使用最新密钥
|
||||
*/
|
||||
private fun getCryptoInterceptor(): CryptoInterceptor {
|
||||
// 清除旧实例,确保使用最新密钥
|
||||
CryptoInterceptor.clearInstance()
|
||||
return CryptoInterceptor.getInstance(
|
||||
publicKey = CryptoKeyManager.requestPublicKey,
|
||||
privateKey = CryptoKeyManager.responsePrivateKey
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新初始化网络客户端(密钥更新后调用)
|
||||
*/
|
||||
fun resetClient() {
|
||||
retrofit = null
|
||||
// 强制重新创建 Retrofit
|
||||
getNetworkService()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.ckkj.defense_device.utils.crypto
|
||||
|
||||
import android.util.Base64
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
/**
|
||||
* AES 加密工具类
|
||||
*
|
||||
* 算法:AES/ECB/PKCS5Padding (Android) 与 AES/ECB/PKCS7Padding (iOS) 兼容
|
||||
*/
|
||||
object AESUtil {
|
||||
|
||||
private const val ALGORITHM = "AES"
|
||||
private const val TRANSFORMATION = "AES/ECB/PKCS5Padding"
|
||||
|
||||
/**
|
||||
* AES ECB 模式加密
|
||||
*
|
||||
* @param key 32字节 ASCII 字符串密钥
|
||||
* @param data UTF-8 编码的明文字节数组
|
||||
* @return Base64 编码的密文字节数组
|
||||
*/
|
||||
fun encrypt(key: String, data: ByteArray): ByteArray {
|
||||
val keySpec = SecretKeySpec(key.toByteArray(Charsets.UTF_8), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keySpec)
|
||||
return cipher.doFinal(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* AES ECB 模式加密,返回 Base64 字符串
|
||||
*
|
||||
* @param key 32字节 ASCII 字符串密钥
|
||||
* @param plainText UTF-8 明文字符串
|
||||
* @return Base64 编码的密文字符串
|
||||
*/
|
||||
fun encrypt(key: String, plainText: String): String {
|
||||
val encrypted = encrypt(key, plainText.toByteArray(Charsets.UTF_8))
|
||||
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
||||
}
|
||||
|
||||
/**
|
||||
* AES ECB 模式解密
|
||||
*
|
||||
* @param key 32字节 ASCII 字符串密钥
|
||||
* @param encryptedData Base64 编码的密文字节数组
|
||||
* @return UTF-8 解密后的明文字节数组
|
||||
*/
|
||||
fun decrypt(key: String, encryptedData: ByteArray): ByteArray {
|
||||
val keySpec = SecretKeySpec(key.toByteArray(Charsets.UTF_8), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec)
|
||||
return cipher.doFinal(encryptedData)
|
||||
}
|
||||
|
||||
/**
|
||||
* AES ECB 模式解密,返回 UTF-8 字符串
|
||||
*
|
||||
* @param key 32字节 ASCII 字符串密钥
|
||||
* @param encryptedText Base64 编码的密文字符串
|
||||
* @return UTF-8 解密后的明文字符串
|
||||
*/
|
||||
fun decrypt(key: String, encryptedText: String): String {
|
||||
val encryptedData = Base64.decode(encryptedText, Base64.NO_WRAP)
|
||||
val decrypted = decrypt(key, encryptedData)
|
||||
return String(decrypted, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机 AES 密钥
|
||||
*
|
||||
* @param length 密钥长度,默认 32 字节
|
||||
* @return 随机 ASCII 字符串密钥
|
||||
*/
|
||||
fun generateKey(length: Int = 32): String {
|
||||
val chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
return (1..length)
|
||||
.map { chars.random() }
|
||||
.joinToString("")
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 编码
|
||||
*/
|
||||
fun base64Encode(data: ByteArray): String {
|
||||
return Base64.encodeToString(data, Base64.NO_WRAP)
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64 解码
|
||||
*/
|
||||
fun base64Decode(encoded: String): ByteArray {
|
||||
return Base64.decode(encoded, Base64.NO_WRAP)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
package com.ckkj.defense_device.utils.crypto
|
||||
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import com.google.gson.JsonParser
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
||||
import java.nio.charset.Charset
|
||||
|
||||
/**
|
||||
* 加密拦截器
|
||||
*
|
||||
* 实现 App 请求加密和服务端响应解密
|
||||
*
|
||||
* 加密流程:
|
||||
* 1. 生成随机 AES 密钥 (32字节 ASCII)
|
||||
* 2. 用 RSA 公钥加密 AES 密钥
|
||||
* 3. 用 AES 加密请求体
|
||||
* 4. 设置请求头 encrypt-key 为 RSA 加密后的 AES 密钥
|
||||
* 5. 请求体为 AES 加密后的 Base64 字符串
|
||||
*
|
||||
* 解密流程(仅需要对响应解密的接口):
|
||||
* 1. 从响应头获取 encrypt-key
|
||||
* 2. 用 RSA 私钥解密得到 AES 密钥
|
||||
* 3. 用 AES 解密响应体
|
||||
*/
|
||||
class CryptoInterceptor private constructor(
|
||||
private val publicKey: String,
|
||||
private val privateKey: String
|
||||
) : Interceptor {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CryptoInterceptor"
|
||||
private const val HEADER_ENCRYPT_KEY = "encrypt-key"
|
||||
private const val HEADER_IS_ENCRYPT = "isEncrypt"
|
||||
private const val CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
||||
|
||||
// 需要加密的接口列表(POST/PUT 请求)
|
||||
private val ENCRYPT_ENDPOINTS = setOf(
|
||||
"app/login",
|
||||
"app/register",
|
||||
"app/forgot",
|
||||
"app/switchFamily",
|
||||
"app/addDevice",
|
||||
"app/updateDeviceInfo",
|
||||
"app/deleteDevice",
|
||||
"app/addDeviceUser",
|
||||
"app/updateUserInfo",
|
||||
"app/forgotPassword",
|
||||
"app/retrievePassword",
|
||||
"app/verifyOptionPassword",
|
||||
"app/obtainInviterInformation",
|
||||
"app/selectRecommendationCode",
|
||||
"app/generateRecommendationCode",
|
||||
"app/revokeInvitation",
|
||||
"app/updateFamilyUser",
|
||||
"app/selectInviterInfo",
|
||||
"app/joinTheFamily",
|
||||
"app/stream/start"
|
||||
)
|
||||
|
||||
// 需要解密响应的接口列表(默认与加密列表对称 —— 加密请求的接口响应也会被加密)
|
||||
// 额外加上 GET 接口(GET 无 body 不加密请求,但后端仍可能加密响应)
|
||||
private val DECRYPT_RESPONSE_ENDPOINTS: Set<String> = ENCRYPT_ENDPOINTS + setOf(
|
||||
"app/selectFamily", // 获取家庭列表(GET)
|
||||
"app/getdeviceList", // 获取设备列表(GET)
|
||||
"app/deviceInfo", // 获取设备信息(GET)
|
||||
"app/appMessage/list", // 消息列表(GET)
|
||||
"app/appMessageInfo", // 告警消息详情(GET)
|
||||
"app/appMessage/deviceMeslist", // 设备告警日志(GET)
|
||||
"app/familyMembers/list", // 家庭成员列表(GET)
|
||||
"app/deviceUser/list", // 设备关联用户列表(GET)
|
||||
"app/userInfo", // 当前用户信息(GET)
|
||||
"app/deleteMessage" // 删除消息(DELETE)
|
||||
)
|
||||
|
||||
@Volatile
|
||||
private var instance: CryptoInterceptor? = null
|
||||
|
||||
fun getInstance(publicKey: String, privateKey: String): CryptoInterceptor {
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: CryptoInterceptor(publicKey, privateKey).also {
|
||||
instance = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearInstance() {
|
||||
instance = null
|
||||
}
|
||||
|
||||
fun isEncryptEndpoint(url: String): Boolean {
|
||||
return ENCRYPT_ENDPOINTS.any { url.contains(it, ignoreCase = true) }
|
||||
}
|
||||
|
||||
fun isDecryptEndpoint(url: String): Boolean {
|
||||
return DECRYPT_RESPONSE_ENDPOINTS.any { url.contains(it, ignoreCase = true) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
val url = request.url.toString()
|
||||
|
||||
// 检查是否需要加密
|
||||
val needsEncrypt = isEncryptEndpoint(url)
|
||||
val needsDecrypt = isDecryptEndpoint(url)
|
||||
|
||||
if (!needsEncrypt && !needsDecrypt) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
// 调试日志
|
||||
Log.d(TAG, "=== CryptoInterceptor Debug ===")
|
||||
Log.d(TAG, "URL: $url")
|
||||
Log.d(TAG, "needsEncrypt: $needsEncrypt, needsDecrypt: $needsDecrypt")
|
||||
Log.d(TAG, "PublicKey available: ${publicKey.isNotBlank()}, length: ${publicKey.length}")
|
||||
Log.d(TAG, "PrivateKey available: ${privateKey.isNotBlank()}, length: ${privateKey.length}")
|
||||
|
||||
// 加密请求
|
||||
val encryptedRequest = if (needsEncrypt) {
|
||||
encryptRequest(request)
|
||||
} else {
|
||||
request
|
||||
}
|
||||
|
||||
// 发起请求
|
||||
val response = try {
|
||||
chain.proceed(encryptedRequest)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Request failed", e)
|
||||
throw e
|
||||
}
|
||||
|
||||
// 解密响应
|
||||
return if (needsDecrypt) {
|
||||
decryptResponse(response, request)
|
||||
} else {
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密请求
|
||||
*/
|
||||
private fun encryptRequest(request: Request): Request {
|
||||
val requestBody = request.body ?: return request
|
||||
|
||||
// 只处理 JSON 请求
|
||||
val contentType = requestBody.contentType()?.toString() ?: ""
|
||||
if (!contentType.contains("application/json")) {
|
||||
return request
|
||||
}
|
||||
|
||||
// 只处理 POST 和 PUT
|
||||
val method = request.method
|
||||
if (method != "POST" && method != "PUT") {
|
||||
return request
|
||||
}
|
||||
|
||||
// 读取原始请求体
|
||||
val buffer = okio.Buffer()
|
||||
requestBody.writeTo(buffer)
|
||||
val originalBody = buffer.readString(Charset.defaultCharset())
|
||||
Log.d(TAG, "Original request body: $originalBody")
|
||||
|
||||
// 生成随机 AES 密钥
|
||||
val aesKey = AESUtil.generateKey(32)
|
||||
Log.d(TAG, "Generated AES key: $aesKey")
|
||||
|
||||
// 1. keyBase64 = Base64(UTF8(aesKey))
|
||||
val keyBase64 = AESUtil.base64Encode(aesKey.toByteArray(Charsets.UTF_8))
|
||||
Log.d(TAG, "keyBase64: $keyBase64")
|
||||
|
||||
// 2. encrypt-key = RSA-PKCS1-Encrypt(请求公钥, UTF8(keyBase64))
|
||||
// encryptByPublicKey(String, String) 已返回 Base64 编码结果
|
||||
val encryptedKeyBase64 = RSAUtil.encryptByPublicKey(publicKey, keyBase64)
|
||||
Log.d(TAG, "Encrypted key (Base64): $encryptedKeyBase64")
|
||||
|
||||
// 3. body = AES-ECB-PKCS5-Encrypt(aesKey, UTF8(JSON))
|
||||
// encrypt(String, String) 已返回 Base64 编码结果
|
||||
val encryptedBodyBase64 = AESUtil.encrypt(aesKey, originalBody)
|
||||
Log.d(TAG, "Encrypted body (Base64): $encryptedBodyBase64")
|
||||
|
||||
// 构建新请求(根据原方法选择 POST 或 PUT)
|
||||
val newRequestBuilder = request.newBuilder()
|
||||
.header(HEADER_ENCRYPT_KEY, encryptedKeyBase64)
|
||||
.header(HEADER_IS_ENCRYPT, "true")
|
||||
|
||||
return if (method == "PUT") {
|
||||
newRequestBuilder.put(encryptedBodyBase64.toRequestBody(CONTENT_TYPE_JSON.toMediaTypeOrNull()))
|
||||
} else {
|
||||
newRequestBuilder.post(encryptedBodyBase64.toRequestBody(CONTENT_TYPE_JSON.toMediaTypeOrNull()))
|
||||
}.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密响应
|
||||
*
|
||||
* 兼容三种后端响应形态:
|
||||
* 1. 普通 JSON(无 encrypt-key 头)→ 原样放行
|
||||
* 2. 整 body 加密(响应头有 encrypt-key 且 body 不是合法 JSON)→ 整体 AES 解密
|
||||
* 3. 嵌套加密(响应头有 encrypt-key,body 是 {code,msg,data:"加密串"})
|
||||
* → 只解 data 字段,保持外层 JSON 结构(与 iOS 端一致)
|
||||
*/
|
||||
private fun decryptResponse(response: Response, originalRequest: Request): Response {
|
||||
val responseBody = response.body ?: return response
|
||||
|
||||
val encryptedKeyBase64 = response.header(HEADER_ENCRYPT_KEY)
|
||||
if (encryptedKeyBase64.isNullOrBlank()) {
|
||||
return response
|
||||
}
|
||||
|
||||
val rawBody: String = try {
|
||||
responseBody.source().apply { request(Long.MAX_VALUE) }
|
||||
.buffer.clone().readString(Charset.defaultCharset())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Read response body failed", e)
|
||||
return response
|
||||
}
|
||||
|
||||
// 先拿到 AES 密钥(RSA 私钥解密 encrypt-key 头)
|
||||
val aesKey = try {
|
||||
val aesKeyBase64 = RSAUtil.decryptByPrivateKeyBase64(privateKey, encryptedKeyBase64)
|
||||
String(Base64.decode(aesKeyBase64, Base64.NO_WRAP), Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "RSA decrypt encrypt-key failed", e)
|
||||
return response
|
||||
}
|
||||
|
||||
// 形态 3:嵌套加密 —— 响应是 JSON,data 字段是加密串
|
||||
val trimmed = rawBody.trim()
|
||||
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
||||
try {
|
||||
val jsonElement = JsonParser().parse(trimmed)
|
||||
if (jsonElement.isJsonObject) {
|
||||
val obj = jsonElement.asJsonObject
|
||||
if (obj.has("data") && obj.get("data").isJsonPrimitive
|
||||
&& obj.get("data").asJsonPrimitive.isString
|
||||
) {
|
||||
val encryptedData = obj.get("data").asString
|
||||
val decryptedData = AESUtil.decrypt(aesKey, encryptedData)
|
||||
// 把 data 字段替换为解密后的 JSON 字符串(让 Gson 当成对象继续解析)
|
||||
obj.add("data", parseDataAsJsonElement(decryptedData))
|
||||
val newBody = obj.toString().toResponseBody(CONTENT_TYPE_JSON.toMediaTypeOrNull())
|
||||
return response.newBuilder()
|
||||
.body(newBody)
|
||||
.removeHeader(HEADER_ENCRYPT_KEY)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Nested decrypt failed, fallback to whole-body decrypt", e)
|
||||
}
|
||||
}
|
||||
|
||||
// 形态 2:整 body 加密
|
||||
return try {
|
||||
val decryptedBody = AESUtil.decrypt(aesKey, rawBody)
|
||||
response.newBuilder()
|
||||
.body(decryptedBody.toResponseBody(CONTENT_TYPE_JSON.toMediaTypeOrNull()))
|
||||
.removeHeader(HEADER_ENCRYPT_KEY)
|
||||
.build()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Whole-body decrypt failed, return raw response", e)
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把解密后的字符串解析成 JsonElement(如果是合法 JSON);否则当成普通字符串返回
|
||||
*/
|
||||
private fun parseDataAsJsonElement(text: String): com.google.gson.JsonElement {
|
||||
val s = text.trim()
|
||||
return try {
|
||||
if ((s.startsWith("{") && s.endsWith("}")) ||
|
||||
(s.startsWith("[") && s.endsWith("]"))
|
||||
) {
|
||||
JsonParser().parse(s)
|
||||
} else {
|
||||
com.google.gson.JsonPrimitive(s)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
com.google.gson.JsonPrimitive(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 扩展函数:String 转 RequestBody
|
||||
private fun String.toRequestBody(mediaType: okhttp3.MediaType?): okhttp3.RequestBody {
|
||||
return okhttp3.RequestBody.create(mediaType, this)
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package com.ckkj.defense_device.utils.crypto
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.blankj.utilcode.util.SPUtils
|
||||
import com.ckkj.defense_device.utils.UserinfoUtil
|
||||
|
||||
/**
|
||||
* 密钥管理类
|
||||
*
|
||||
* 管理 RSA 加密所需的公钥和私钥
|
||||
*
|
||||
* 密钥关系:
|
||||
* ┌─────────────┬──────────────────┬──────────────────┐
|
||||
* │ 方向 │ App 持有 │ 服务端持有 │
|
||||
* ├─────────────┼──────────────────┼──────────────────┤
|
||||
* │ App请求服务端 │ 请求公钥 (RSA-P) │ 请求私钥 (RSA-S) │
|
||||
* ├─────────────┼──────────────────┼──────────────────┤
|
||||
* │ 服务端响应App │ 响应私钥 (RSA-S) │ 响应公钥 (RSA-P) │
|
||||
* └─────────────┴──────────────────┴──────────────────┘
|
||||
*
|
||||
* 需要生成两套独立 RSA 2048/3072 密钥对
|
||||
*/
|
||||
object CryptoKeyManager {
|
||||
|
||||
private const val TAG = "CryptoKeyManager"
|
||||
|
||||
// SPUtils 用于持久化密钥配置
|
||||
private val spUtils: SPUtils by lazy {
|
||||
SPUtils.getInstance("crypto_keys")
|
||||
}
|
||||
|
||||
// 配置项 Keys
|
||||
private const val KEY_REQUEST_PUBLIC_KEY = "request_public_key"
|
||||
private const val KEY_RESPONSE_PRIVATE_KEY = "response_private_key"
|
||||
private const val KEY_CRYPTO_ENABLED = "crypto_enabled"
|
||||
|
||||
// 配置项默认值
|
||||
// 注意:这些密钥必须与后端配置配对使用
|
||||
// App端公钥 = 后端私钥(App加密请求,后端解密)
|
||||
// App端私钥 = 后端公钥(后端加密响应,App解密)
|
||||
private const val DEFAULT_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVIQm+1AWMfkT1uVOyugKQwBeHtgpulABOrVfaL7Xzc1/rKSdLdjYZLZnnOU+rdE1jqz20MXDBozK22spH/j53d9nFLftHkMCRUlfc0MWEqtSla+2MdzElIHEbq4CSizZOcT8QXCRw7V2OuDqnC7XRQmnpA5Ks1WaKH5V0aoKWztjsqZV6vVWPqyDC94mvu/Q/9e6KVfkqAzR+afuo9OoBFewDGQ6jfqyu1TxoGZrfSr5lXOVeADf0CVfWCDze5y4f7er4JjPHWA2+yv8Scd9bqVJ4Q+/yR2Gd516Mi11OgBKc8GdkuA4/kfmXc7n91SMjgQP6DlTVuPc/6KnB/pyQIDAQAB"
|
||||
private const val DEFAULT_PRIVATE_KEY = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYg1dC9XDMRtKJ5IFj+Hd3bq7+r2YL1y8FM9HD111PN/YVy4K6O67cRA/TLG7+tl6eYiA73f5lzPgv1riOwBPSCmvVopxq2KiJs30uEnug0qGp6l2cwQOmjitFAQF8dOtAl4m6EM/jdXCZYbQrthJ6n3tR+vGj2IrGSJEjPrj3VRljK9I7Zw0Be0rhJn/vZw2CA4GoVfE/3gqa5bpl+3SFxMP7TwF5uZueCNqLaPnd7jK5jB1SUghI7+pq897Ng4EG6kdOGsiQe5kbHWkSPTmPEObMwgLJ18i/XCVg+LUkTDJOC/MAfB+WGcKryNHrhcbhB6+XstpoGT8JlOPOgh0BAgMBAAECggEBALZG7MH8bDguL7XTYHNPjRRJZJ4aAGlbgcR5edHMkEvPKyfyK16qPG5IBqKcN93Mnx/sMIL2Q+RkiVWNCdd0MbuU9m/m9JSnTkyPhYWyHc6pRV0NaD496Nrhud/gFuY2cI/yhArXeI1gI6mdrddW83u0pFfCUojEFyETmsz3UzU1LMkVbwCXNbZ1b3GfcVnONMn8T/7CMlRPlKyOuE0OPnTut4wddAybe5BKDQYnGzuosUdO+Q+2K3hqaTD6Bb33D4ATHjvR3Vt9itE07BiaBFjZkE2odP4x3fSsnc7Cw3A7zHQivpfgqcoBcGUaMcN1FdgpaF2mbe9/eHn/lYCpevkCgYEA4FoKq3fPOITAtA6bjwCHXvIQUjv4vQ0BZtt7OX7zwpRwqOhJfN0ZRkd7NMUqYnB10N9NgPRz0kKC6qff5duN+zkYIzjHrlm5lzdIEAouovRlrGXST4n8gAYteh5D5Or9wRXj7USQoaNyiWKB9A/07ortJvul09sPjJU7IVfzzXMCgYEA9w45Gh2bAvYvm2bWieKZRhcV6Pwy9R8587qmKLC7V/0gfVojBHx7xTmTEfKCGrt3fK4jB5v/2Dar8gDF+RM7g01u0YuFJt17jeFr/4o5KMimfXhoEulDbgjfvhxQOrtkrF5iHpKxiEXqIPiH+B7155PAnE43011AAJCBSylmTrsCgYEAkxr3PA9HFKwXHvklDtMt8BeQlBs2sd9BOAxZ9A1GECP86wPEi9b9p5NfOe6+J+XNmrOQwimHeCqcZPjGWpVnt35sUUv9wlia1Igu/DVw9vCBalUpXXYA1oE2eIg3xHZBBMYxuXXnz9S4WVT6GOoNlAwMDC+dQBi3TVrcdrSQ6/kCgYBA254ICh+ovmKvJGdMGY3thZ/94z+pdEIthyGZ6xOzvMMrxV8ODXQcycmfW4/mXrK2q6yMkdqvs2KejK9sfS0RgmGGZ19UXa7TB4vnsSziVRLIO7TuyggmufOrIBm74XhDfB+8MPykbt5RO43OiKo72mElZ69mMMPdohIfXkX6gwKBgH4L9qJRhNunWSfHO4/8dST+2meSV+Cpqf0b/9cV6v5FMS22m+szzW9XoBXnI9dbAqiEDlCHCLw34pRlC3OzBNlbT6HzNmYXLko2Ttg9thiDq7WS5tV+EHJRP6kLabYGq0Aghtq+6LIwwogbHzhRi8Q52o3CNmM9lThNWZIkzaBe"
|
||||
/**
|
||||
* 是否启用加密
|
||||
*/
|
||||
var isCryptoEnabled: Boolean
|
||||
get() {
|
||||
val stored = spUtils.getBoolean(KEY_CRYPTO_ENABLED, false)
|
||||
// 如果存储值为 false 但密钥已配置,也返回 true
|
||||
return stored || (DEFAULT_PUBLIC_KEY.isNotBlank() && DEFAULT_PRIVATE_KEY.isNotBlank())
|
||||
}
|
||||
set(value) {
|
||||
spUtils.put(KEY_CRYPTO_ENABLED, value)
|
||||
Log.d(TAG, "Crypto enabled: $value")
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求公钥(用于 App 加密请求)
|
||||
* App 使用此公钥加密 AES 密钥,服务端使用对应的私钥解密
|
||||
*/
|
||||
var requestPublicKey: String
|
||||
get() {
|
||||
val stored = spUtils.getString(KEY_REQUEST_PUBLIC_KEY, "")
|
||||
return if (stored.isNotBlank()) stored else DEFAULT_PUBLIC_KEY
|
||||
}
|
||||
set(value) {
|
||||
spUtils.put(KEY_REQUEST_PUBLIC_KEY, value)
|
||||
Log.d(TAG, "Request public key updated: ${value.take(20)}...")
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应私钥(用于 App 解密响应)
|
||||
* 服务端使用对应的响应公钥加密响应,App 使用此私钥解密
|
||||
*/
|
||||
var responsePrivateKey: String
|
||||
get() {
|
||||
val stored = spUtils.getString(KEY_RESPONSE_PRIVATE_KEY, "")
|
||||
return if (stored.isNotBlank()) stored else DEFAULT_PRIVATE_KEY
|
||||
}
|
||||
set(value) {
|
||||
spUtils.put(KEY_RESPONSE_PRIVATE_KEY, value)
|
||||
Log.d(TAG, "Response private key updated: ${value.take(20)}...")
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已配置密钥
|
||||
*/
|
||||
fun isKeyConfigured(): Boolean {
|
||||
// 检查密钥是否有内容(默认值或用户配置)
|
||||
val hasPublicKey = requestPublicKey.isNotBlank()
|
||||
val hasPrivateKey = responsePrivateKey.isNotBlank()
|
||||
Log.d(TAG, "isKeyConfigured: publicKey=$hasPublicKey (length=${requestPublicKey.length}), privateKey=$hasPrivateKey (length=${responsePrivateKey.length})")
|
||||
return hasPublicKey && hasPrivateKey
|
||||
}
|
||||
|
||||
/**
|
||||
* 从服务端配置初始化密钥
|
||||
*
|
||||
* 配置格式(可从服务端接口或配置中心获取):
|
||||
* api-decrypt:
|
||||
* enabled: true
|
||||
* headerFlag: encrypt-key
|
||||
* publicKey: ${APP_RESPONSE_PUBLIC_KEY}
|
||||
* privateKey: ${APP_REQUEST_PRIVATE_KEY}
|
||||
*
|
||||
* @param enabled 是否启用加密
|
||||
* @param publicKey 服务端响应公钥(用于 App 加密请求)
|
||||
* @param privateKey 服务端请求私钥(用于 App 解密响应)
|
||||
*/
|
||||
fun initialize(enabled: Boolean, publicKey: String, privateKey: String) {
|
||||
isCryptoEnabled = enabled
|
||||
requestPublicKey = publicKey
|
||||
responsePrivateKey = privateKey
|
||||
|
||||
Log.i(TAG, "CryptoKeyManager initialized: enabled=$enabled, keyConfigured=${isKeyConfigured()}")
|
||||
}
|
||||
|
||||
/**
|
||||
* 从用户登录信息初始化密钥
|
||||
* 适用于密钥存储在用户账户中的场景
|
||||
*
|
||||
* @param publicKey 公钥
|
||||
* @param privateKey 私钥
|
||||
*/
|
||||
fun initializeFromLogin(publicKey: String, privateKey: String) {
|
||||
initialize(
|
||||
enabled = publicKey.isNotBlank() && privateKey.isNotBlank(),
|
||||
publicKey = publicKey,
|
||||
privateKey = privateKey
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除密钥配置
|
||||
*/
|
||||
fun clear() {
|
||||
spUtils.clear()
|
||||
Log.i(TAG, "CryptoKeyManager cleared")
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取密钥信息摘要(用于调试)
|
||||
*/
|
||||
fun getKeySummary(): String {
|
||||
return buildString {
|
||||
appendLine("CryptoKeyManager Summary:")
|
||||
appendLine(" Enabled: $isCryptoEnabled")
|
||||
appendLine(" RequestPublicKey: ${if (requestPublicKey.isNotBlank()) "${requestPublicKey.take(20)}..." else "NOT SET"}")
|
||||
appendLine(" ResponsePrivateKey: ${if (responsePrivateKey.isNotBlank()) "${responsePrivateKey.take(20)}..." else "NOT SET"}")
|
||||
appendLine(" Configured: ${isKeyConfigured()}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证密钥格式
|
||||
*
|
||||
* @return true 如果密钥是有效的 Base64 编码
|
||||
*/
|
||||
fun validateKeys(): Pair<Boolean, String> {
|
||||
val publicKeyValid = validateBase64Key(requestPublicKey)
|
||||
val privateKeyValid = validateBase64Key(responsePrivateKey)
|
||||
|
||||
return when {
|
||||
!publicKeyValid && !privateKeyValid -> false to "Both keys are invalid"
|
||||
!publicKeyValid -> false to "Request public key is invalid"
|
||||
!privateKeyValid -> false to "Response private key is invalid"
|
||||
else -> true to "Keys are valid"
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateBase64Key(key: String): Boolean {
|
||||
if (key.isBlank()) return false
|
||||
return try {
|
||||
android.util.Base64.decode(key, android.util.Base64.NO_WRAP)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.ckkj.defense_device.utils.crypto
|
||||
|
||||
import android.util.Base64
|
||||
import java.security.KeyFactory
|
||||
import java.security.PublicKey
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.security.spec.RSAPrivateKeySpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import javax.crypto.Cipher
|
||||
|
||||
/**
|
||||
* RSA 加密工具类
|
||||
*
|
||||
* 算法:RSA/ECB/PKCS1Padding
|
||||
* 支持 RSA 2048/3072 位密钥
|
||||
*/
|
||||
object RSAUtil {
|
||||
|
||||
private const val ALGORITHM = "RSA"
|
||||
private const val TRANSFORMATION = "RSA/ECB/PKCS1Padding"
|
||||
|
||||
/**
|
||||
* 使用公钥加密(用于 App 加密请求)
|
||||
*
|
||||
* @param publicKeyStr Base64 编码的公钥字符串
|
||||
* @param data UTF-8 编码的明文字节数组
|
||||
* @return 密文字节数组
|
||||
*/
|
||||
fun encryptByPublicKey(publicKeyStr: String, data: ByteArray): ByteArray {
|
||||
val publicKey = parsePublicKey(publicKeyStr)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
|
||||
return cipher.doFinal(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用公钥加密,返回 Base64 字符串
|
||||
*
|
||||
* @param publicKeyStr Base64 编码的公钥字符串
|
||||
* @param plainText UTF-8 明文字符串
|
||||
* @return Base64 编码的密文字符串
|
||||
*/
|
||||
fun encryptByPublicKey(publicKeyStr: String, plainText: String): String {
|
||||
val encrypted = encryptByPublicKey(publicKeyStr, plainText.toByteArray(Charsets.UTF_8))
|
||||
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密(用于 App 解密响应)
|
||||
*
|
||||
* @param privateKeySpec RSA 私钥规格
|
||||
* @param encryptedData 密文字节数组
|
||||
* @return UTF-8 解密后的明文字节数组
|
||||
*/
|
||||
fun decryptByPrivateKey(privateKeySpec: RSAPrivateKeySpec, encryptedData: ByteArray): ByteArray {
|
||||
val keyFactory = KeyFactory.getInstance(ALGORITHM)
|
||||
val privateKey = keyFactory.generatePrivate(privateKeySpec)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
||||
return cipher.doFinal(encryptedData)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用私钥解密,返回 UTF-8 字符串
|
||||
*
|
||||
* @param privateKeySpec RSA 私钥规格
|
||||
* @param encryptedText Base64 编码的密文字符串
|
||||
* @return UTF-8 解密后的明文字符串
|
||||
*/
|
||||
fun decryptByPrivateKey(privateKeySpec: RSAPrivateKeySpec, encryptedText: String): String {
|
||||
val encryptedData = Base64.decode(encryptedText, Base64.NO_WRAP)
|
||||
val decrypted = decryptByPrivateKey(privateKeySpec, encryptedData)
|
||||
return String(decrypted, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Base64 编码的 PKCS#8 私钥字符串解密
|
||||
*
|
||||
* @param privateKeyBase64 Base64 编码的 PKCS#8 私钥
|
||||
* @param encryptedText Base64 编码的密文字符串
|
||||
* @return UTF-8 解密后的明文字符串
|
||||
*/
|
||||
fun decryptByPrivateKeyBase64(privateKeyBase64: String, encryptedText: String): String {
|
||||
val encryptedData = Base64.decode(encryptedText, Base64.NO_WRAP)
|
||||
val keyFactory = KeyFactory.getInstance(ALGORITHM)
|
||||
val privateKeySpec = PKCS8EncodedKeySpec(Base64.decode(privateKeyBase64, Base64.NO_WRAP))
|
||||
val privateKey = keyFactory.generatePrivate(privateKeySpec)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
||||
val decrypted = cipher.doFinal(encryptedData)
|
||||
return String(decrypted, Charsets.UTF_8)
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析公钥
|
||||
*
|
||||
* @param publicKeyStr Base64 编码的 X.509 公钥
|
||||
* @return PublicKey 对象
|
||||
*/
|
||||
fun parsePublicKey(publicKeyStr: String): PublicKey {
|
||||
val keyBytes = Base64.decode(publicKeyStr, Base64.NO_WRAP)
|
||||
val keySpec = X509EncodedKeySpec(keyBytes)
|
||||
val keyFactory = KeyFactory.getInstance(ALGORITHM)
|
||||
return keyFactory.generatePublic(keySpec)
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Base64 编码的 PKCS#8 私钥创建私钥规格
|
||||
*
|
||||
* @param privateKeyBase64 Base64 编码的 PKCS#8 私钥
|
||||
* @return RSAPrivateKeySpec
|
||||
*/
|
||||
fun createPrivateKeySpecFromBase64(privateKeyBase64: String): RSAPrivateKeySpec {
|
||||
val keyBytes = Base64.decode(privateKeyBase64, Base64.NO_WRAP)
|
||||
val keyFactory = KeyFactory.getInstance(ALGORITHM)
|
||||
val keySpec = keyFactory.getKeySpec(
|
||||
keyFactory.generatePrivate(PKCS8EncodedKeySpec(keyBytes)),
|
||||
RSAPrivateKeySpec::class.java
|
||||
)
|
||||
return keySpec
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
@@ -13,6 +15,13 @@ class LoadingDialog(context: Context) {
|
||||
|
||||
private val dialog: Dialog = Dialog(context)
|
||||
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 {
|
||||
binding = DialogLoadingBinding.inflate(LayoutInflater.from(context))
|
||||
@@ -35,14 +44,23 @@ class LoadingDialog(context: Context) {
|
||||
}
|
||||
|
||||
fun show() {
|
||||
handler.removeCallbacks(dismissRunnable)
|
||||
showTime = System.currentTimeMillis()
|
||||
if (!dialog.isShowing) {
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
if (dialog.isShowing) {
|
||||
dialog.dismiss()
|
||||
handler.removeCallbacks(dismissRunnable)
|
||||
val elapsed = System.currentTimeMillis() - showTime
|
||||
val MIN_SHOW_TIME = 1000L
|
||||
if (elapsed >= MIN_SHOW_TIME) {
|
||||
if (dialog.isShowing) {
|
||||
dialog.dismiss()
|
||||
}
|
||||
} else {
|
||||
handler.postDelayed(dismissRunnable, MIN_SHOW_TIME - elapsed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.ckkj.defense_device.view
|
||||
|
||||
import android.util.Log
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import com.ckkj.defense_device.R
|
||||
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
||||
@@ -12,6 +14,18 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : 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) {
|
||||
super.init(context)
|
||||
startButton.visibility = View.GONE
|
||||
@@ -76,10 +90,54 @@ class MyGSYVideoPlayerLive @JvmOverloads constructor(
|
||||
|
||||
overlay.findViewById<View>(R.id.btnCapture)
|
||||
?.setOnClickListener {
|
||||
|
||||
Log.d("LV", "btnCapture clicked, onShoot invoked")
|
||||
onPtzControlListener?.onShoot()
|
||||
}
|
||||
|
||||
player.initPtzControls(overlay)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/color/send_code_text_color.xml
Normal file
5
app/src/main/res/color/send_code_text_color.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false" android:color="@color/white"/>
|
||||
<item android:color="@color/white"/>
|
||||
</selector>
|
||||
@@ -5,5 +5,5 @@
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/green" />
|
||||
<corners android:radius="8dp" />
|
||||
<corners android:radius="25dp" />
|
||||
</shape>
|
||||
@@ -2,5 +2,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/green" />
|
||||
<corners android:radius="8dp" />
|
||||
<corners android:radius="25dp" />
|
||||
</shape>
|
||||
15
app/src/main/res/drawable/btn_selector_red.xml
Normal file
15
app/src/main/res/drawable/btn_selector_red.xml
Normal 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>
|
||||
10
app/src/main/res/drawable/ic_warning.xml
Normal file
10
app/src/main/res/drawable/ic_warning.xml
Normal 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>
|
||||
@@ -2,5 +2,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/green"/>
|
||||
<corners android:radius="16dp"/>
|
||||
<corners android:radius="4dp"/>
|
||||
</shape>
|
||||
15
app/src/main/res/drawable/send_code_btn_selector.xml
Normal file
15
app/src/main/res/drawable/send_code_btn_selector.xml
Normal 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_enabled="false">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/gray"/>
|
||||
<corners android:radius="99dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/green"/>
|
||||
<corners android:radius="99dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
6
app/src/main/res/drawable/shimmer_placeholder.xml
Normal file
6
app/src/main/res/drawable/shimmer_placeholder.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#E8E8E8" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
@@ -2,5 +2,5 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white"/>
|
||||
<corners android:radius="10dp"/>
|
||||
<corners android:radius="99dp"/>
|
||||
</shape>
|
||||
@@ -73,7 +73,7 @@
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<!-- 列表区域 -->
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
<com.ckkj.defense_device.view.PullRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
@@ -84,8 +84,10 @@
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp"/>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
android:paddingBottom="16dp"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"/>
|
||||
</com.ckkj.defense_device.view.PullRefreshLayout>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<LinearLayout
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/login_title"
|
||||
android:textSize="26dp"
|
||||
android:textSize="22dp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/black"
|
||||
app:layout_constraintTop_toBottomOf="@id/logoFrame"
|
||||
@@ -77,7 +77,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/phoneRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
app:layout_constraintTop_toBottomOf="@+id/tvSmsLogin"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -99,6 +99,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:hint="@string/phone_hint"
|
||||
android:textSize="14dp"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@android:color/black"
|
||||
android:background="@null"
|
||||
@@ -116,7 +117,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/smsRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -146,28 +147,31 @@
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@android:color/black"
|
||||
android:background="@null"
|
||||
android:inputType="number"
|
||||
android:paddingVertical="16dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivSmsIcon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sendCodeBtn"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
android:textSize="14dp"
|
||||
android:inputType="number"
|
||||
android:paddingVertical="16dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivSmsIcon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sendCodeBtn"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<!-- 发送验证码按钮 -->
|
||||
<android.widget.Button
|
||||
android:id="@+id/sendCodeBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="110dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@drawable/send_code_btn_selector"
|
||||
android:elevation="0dp"
|
||||
android:gravity="center"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:text="@string/send_sms_code"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:maxWidth="120dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:textColor="@color/send_code_text_color"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="12dp" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -175,7 +179,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/passwordRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -199,11 +203,12 @@
|
||||
<EditText
|
||||
android:id="@+id/etPassword"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:hint="@string/password_hint"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14dp"
|
||||
android:background="@null"
|
||||
android:inputType="textPassword"
|
||||
android:paddingVertical="16dp"
|
||||
@@ -211,7 +216,6 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -219,13 +223,15 @@
|
||||
<android.widget.Button
|
||||
android:id="@+id/loginBtn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:text="@string/login"
|
||||
android:textSize="18sp"
|
||||
android:textSize="14dp"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:textColor="@color/white"
|
||||
app:layout_constraintTop_toBottomOf="@+id/smsRowLayout"
|
||||
android:layout_marginTop="40dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
@@ -235,7 +241,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/register"
|
||||
android:textSize="14sp"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/green"
|
||||
android:padding="8dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginBtn"
|
||||
|
||||
@@ -123,6 +123,8 @@
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -77,10 +77,12 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:background="@drawable/send_code_btn_selector"
|
||||
android:text="@string/select_wifi"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp" />
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:textSize="14dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -42,8 +42,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="12dp"
|
||||
android:layout_marginTop="8dp">
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -88,8 +87,41 @@
|
||||
android:paddingBottom="16dp"/>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<!-- 空页面 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoPermission"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="244dp"
|
||||
android:layout_height="131dp"
|
||||
android:src="@mipmap/default_page"/>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
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
|
||||
android:id="@+id/butting"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
@@ -102,6 +134,8 @@
|
||||
android:text="@string/confirm"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14dp"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
android:background="#FFFFFF" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyLayout"
|
||||
android:id="@+id/layoutNoPermission"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
@@ -116,6 +116,22 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/no_message" />
|
||||
</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>
|
||||
@@ -36,10 +36,6 @@
|
||||
android:layout_height="24dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp"
|
||||
android:background="@color/face" />
|
||||
|
||||
<LinearLayout
|
||||
android:gravity="center_vertical"
|
||||
|
||||
153
app/src/main/res/layout/cancel_account_activity.xml
Normal file
153
app/src/main/res/layout/cancel_account_activity.xml
Normal 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>
|
||||
@@ -105,6 +105,8 @@
|
||||
android:textAllCaps="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/etConfirmPassword" />
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginStart="30dp"
|
||||
android:layout_marginEnd="30dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_height="30dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
@@ -21,6 +21,7 @@
|
||||
android:text="@string/cancel"
|
||||
android:textColor="@color/gray"
|
||||
android:textSize="14dp"
|
||||
android:textStyle="bold"
|
||||
android:clickable="true"
|
||||
android:focusable="true" />
|
||||
|
||||
@@ -37,6 +38,7 @@
|
||||
android:text="@string/confirm"
|
||||
android:textColor="@color/green"
|
||||
android:textSize="14dp"
|
||||
android:textStyle="bold"
|
||||
android:clickable="true"
|
||||
android:focusable="true" />
|
||||
</LinearLayout>
|
||||
@@ -45,6 +47,7 @@
|
||||
android:id="@+id/rvMessageList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxHeight="150dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingTop="12dp"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/uploading"
|
||||
android:textColor="@color/black"
|
||||
android:textColor="@color/green"
|
||||
android:textSize="16sp"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -20,18 +20,11 @@
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/dialog_view" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvWifiList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_weight="1"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvWifiList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -287,6 +287,8 @@
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14dp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
@@ -107,6 +107,8 @@
|
||||
android:textSize="17sp"
|
||||
android:textStyle="bold"
|
||||
android:background="@drawable/bg_capture_face_button"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:textColor="#F2FFFFFF" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
<ImageView
|
||||
android:id="@+id/backIcon"
|
||||
android:layout_width="24dp"
|
||||
@@ -60,7 +58,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/phoneRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -90,7 +88,7 @@
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivPhoneIcon"
|
||||
@@ -102,7 +100,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/smsRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -133,6 +131,7 @@
|
||||
android:textColor="@android:color/black"
|
||||
android:background="@null"
|
||||
android:inputType="number"
|
||||
android:textSize="14dp"
|
||||
android:paddingVertical="16dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivSmsIcon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/sendCodeBtn"
|
||||
@@ -142,14 +141,17 @@
|
||||
<!-- 发送验证码按钮 -->
|
||||
<android.widget.Button
|
||||
android:id="@+id/sendCodeBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/send_code"
|
||||
android:textSize="16sp"
|
||||
android:layout_width="110dp"
|
||||
android:layout_height="36dp"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/send_sms_code"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:maxWidth="120dp"
|
||||
android:background="@drawable/send_code_btn_selector"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:gravity="center"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -161,7 +163,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/passwordRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -175,6 +177,7 @@
|
||||
android:id="@+id/ivPasswordIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/ma"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
@@ -190,6 +193,7 @@
|
||||
android:textColor="@android:color/black"
|
||||
android:background="@null"
|
||||
android:inputType="text"
|
||||
android:textSize="14dp"
|
||||
android:paddingVertical="16dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivPasswordIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -203,7 +207,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/inviteRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -216,6 +220,7 @@
|
||||
android:id="@+id/ivInviteIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/ma"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
@@ -232,7 +237,7 @@
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivInviteIcon"
|
||||
@@ -244,13 +249,15 @@
|
||||
<android.widget.Button
|
||||
android:id="@+id/btnNext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/determine"
|
||||
android:text="@string/determine"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="16sp"
|
||||
android:textSize="16dp"
|
||||
app:cornerRadius="12dp"
|
||||
android:textColor="@color/white"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:background="@mipmap/home_back"
|
||||
tools:context=".home.HomeMenuFragment">
|
||||
|
||||
<LinearLayout
|
||||
@@ -36,15 +38,13 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCurrentFamily"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/my_family_home"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20dp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
android:textStyle="bold" />
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivFamilyArrow"
|
||||
@@ -54,11 +54,6 @@
|
||||
android:src="@mipmap/triangle" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -70,10 +65,10 @@
|
||||
android:layout_width="38dp"
|
||||
android:layout_height="38dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:background="@drawable/bg_home_top_icon_light"
|
||||
android:padding="7dp">
|
||||
android:background="@drawable/bg_home_top_icon_light">
|
||||
|
||||
<ImageView
|
||||
android:padding="7dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@mipmap/message"/>
|
||||
@@ -81,15 +76,13 @@
|
||||
<TextView
|
||||
android:id="@+id/tvUnreadCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="16dp"
|
||||
android:layout_gravity="end|top"
|
||||
android:background="@drawable/bg_unread_dot"
|
||||
android:minWidth="16dp"
|
||||
android:height="16dp"
|
||||
android:gravity="center"
|
||||
android:textSize="10sp"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone" />
|
||||
android:textSize="10dp"
|
||||
android:textColor="@color/white"/>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
@@ -125,7 +118,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/device"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16dp"
|
||||
@@ -133,11 +125,15 @@
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
<com.ckkj.defense_device.view.PullRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/transparent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvDeviceList"
|
||||
@@ -147,10 +143,11 @@
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:clipToPadding="false"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingBottom="16dp"
|
||||
android:background="@android:color/transparent"
|
||||
tools:listitem="@layout/item_device" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</com.ckkj.defense_device.view.PullRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoPermission"
|
||||
@@ -168,6 +165,22 @@
|
||||
android:src="@mipmap/default_page" />
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
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>
|
||||
|
||||
<View
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
android:id="@+id/llExpireTime"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
@@ -118,6 +117,7 @@
|
||||
</FrameLayout>
|
||||
<!-- 使用智能文字和图标 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/sys"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
@@ -125,16 +125,16 @@
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@drawable/syis" />
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@mipmap/syis" />
|
||||
|
||||
<TextView
|
||||
android:textColor="@color/green"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="@string/use_smart_defense_app"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -196,9 +196,10 @@
|
||||
<!-- 底部按钮区域 -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="42dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="bottom">
|
||||
@@ -211,6 +212,8 @@
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="@string/cancel_invite"
|
||||
android:textColor="@color/green"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:textSize="16sp"
|
||||
android:background="@drawable/bg_blue_border_round"
|
||||
android:textAllCaps="false" />
|
||||
@@ -223,6 +226,8 @@
|
||||
android:text="@string/regenerate"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/bg_blue_round"
|
||||
android:textAllCaps="false" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
android:id="@+id/itemFamily1"
|
||||
android:layout_width="match_parent"
|
||||
android:gravity="center"
|
||||
android:layout_height="30dp">
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:paddingVertical="10dp"
|
||||
android:id="@+id/tvFamilyName"
|
||||
android:gravity="center"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/my_family"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16dp" />
|
||||
android:textSize="14dp" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
90
app/src/main/res/layout/join_family_invite_activity.xml
Normal file
90
app/src/main/res/layout/join_family_invite_activity.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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"
|
||||
tools:context=".personal.JoinFamilyInviteActivity">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/topBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivBack"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:contentDescription="@string/back"
|
||||
android:src="@mipmap/back"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contentLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="32dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/btnJoinNow"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar"
|
||||
app:layout_constraintVertical_bias="0.35">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/ivAvatar"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
android:contentDescription="@string/avatar"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@mipmap/img"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvNickname"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/nickname_placeholder"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInviteMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/invite_join_family_message_preview"
|
||||
android:textColor="@color/gray"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<android.widget.Button
|
||||
android:id="@+id/btnJoinNow"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="40dp"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:text="@string/join_now"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
24
app/src/main/res/layout/layout_refresh_header.xml
Normal file
24
app/src/main/res/layout/layout_refresh_header.xml
Normal 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>
|
||||
@@ -35,60 +35,109 @@
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/searchLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="12dp"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_user_manage"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@mipmap/search"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@null"
|
||||
android:hint="@string/search_preset_text"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:inputType="text"
|
||||
android:imeOptions="actionSearch"
|
||||
android:singleLine="true"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<!-- 列表区域 -->
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
<com.yanzhenjie.recyclerview.SwipeRecyclerView
|
||||
android:id="@+id/rvLinkedUser"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/searchLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/white"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp"/>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
android:paddingVertical="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@drawable/bg_user_manage"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingHorizontal="16dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@mipmap/search"
|
||||
android:layout_marginRight="8dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@null"
|
||||
android:hint="@string/search_preset_text"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:inputType="text"
|
||||
android:imeOptions="actionSearch"
|
||||
android:singleLine="true"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<!-- 列表 -->
|
||||
<com.ckkj.defense_device.view.PullRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.yanzhenjie.recyclerview.SwipeRecyclerView
|
||||
android:id="@+id/rvLinkedUser"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingBottom="16dp"/>
|
||||
</com.ckkj.defense_device.view.PullRefreshLayout>
|
||||
|
||||
<!-- 空页面 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoPermission"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="244dp"
|
||||
android:layout_height="131dp"
|
||||
android:src="@mipmap/default_page"/>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
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>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/butting"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -102,6 +151,8 @@
|
||||
android:text="@string/add_linked_user"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14dp"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg" />
|
||||
<!-- 悬浮加号按钮 -->
|
||||
<ImageView
|
||||
|
||||
@@ -252,6 +252,39 @@
|
||||
android:background="#FFFFFF"
|
||||
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>
|
||||
|
||||
|
||||
@@ -39,13 +39,23 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvMessageList"
|
||||
<com.ckkj.defense_device.view.PullRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
tools:listitem="@layout/item_message_notice" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvMessageList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="never"
|
||||
android:paddingBottom="16dp"
|
||||
tools:listitem="@layout/item_message_notice" />
|
||||
|
||||
</com.ckkj.defense_device.view.PullRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/emptyLayout"
|
||||
@@ -58,7 +68,22 @@
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@mipmap/no_message" />
|
||||
android:src="@mipmap/default_page_img_name" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNoNetwork"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="32dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="244dp"
|
||||
android:layout_height="131dp"
|
||||
android:src="@mipmap/no_network_img_name" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
android:id="@+id/tvNickname"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/enter_nickname"
|
||||
android:text=""
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16dp"
|
||||
android:textStyle="bold" />
|
||||
@@ -54,10 +54,11 @@
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:text="@string/admin"
|
||||
android:text=""
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold" />
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
@@ -65,10 +66,19 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="13589898989"
|
||||
android:text=""
|
||||
android:textColor="#666666"
|
||||
android:textSize="16dp" />
|
||||
</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
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/phoneRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -89,7 +89,7 @@
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivPhoneIcon"
|
||||
@@ -100,7 +100,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/smsRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -129,6 +129,7 @@
|
||||
android:hint="@string/sms_code_hint"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="14dp"
|
||||
android:background="@null"
|
||||
android:inputType="number"
|
||||
android:paddingVertical="16dp"
|
||||
@@ -140,14 +141,17 @@
|
||||
<!-- 发送验证码按钮 -->
|
||||
<android.widget.Button
|
||||
android:id="@+id/sendCodeBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="110dp"
|
||||
android:layout_height="36dp"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:text="@string/send_sms_code"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
android:textColor="@color/white"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:maxWidth="120dp"
|
||||
android:background="@drawable/send_code_btn_selector"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:gravity="center"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
@@ -159,7 +163,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/idRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -189,7 +193,7 @@
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivIdIcon"
|
||||
@@ -201,7 +205,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/passwordRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -232,6 +236,7 @@
|
||||
android:background="@null"
|
||||
android:inputType="text"
|
||||
android:paddingVertical="16dp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivPasswordIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -244,7 +249,7 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/inviteRowLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/textinput_white_bg"
|
||||
android:paddingStart="16dp"
|
||||
@@ -274,7 +279,7 @@
|
||||
android:paddingVertical="16dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:textColorHint="@color/gray"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/ivInviteIcon"
|
||||
@@ -286,13 +291,15 @@
|
||||
<android.widget.Button
|
||||
android:id="@+id/btnNext"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="56dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:text="@string/next_step"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="16sp"
|
||||
android:textSize="16dp"
|
||||
app:cornerRadius="12dp"
|
||||
android:textColor="@color/white"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -35,6 +35,7 @@ tools:context=".login.RegisterSuccessActivity">
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
|
||||
app:layout_constraintBottom_toBottomOf="@id/backIcon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -80,6 +81,8 @@ tools:context=".login.RegisterSuccessActivity">
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14dp"
|
||||
android:textStyle="bold"
|
||||
android:elevation="0dp"
|
||||
android:stateListAnimator="@null"
|
||||
android:background="@drawable/rounded_blue_bg"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user