diff --git a/.kotlin/errors/errors-1783384781223.log b/.kotlin/errors/errors-1783384781223.log new file mode 100644 index 0000000..d276cc5 --- /dev/null +++ b/.kotlin/errors/errors-1783384781223.log @@ -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) + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6fb2a45..bc06227 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,6 +28,12 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.DefenseApplication"> + + @@ -122,8 +128,7 @@ android:exported="false" /> - + android:exported="false" /> diff --git a/app/src/main/java/com/ckkj/defense_device/home/PersonalFragment.kt b/app/src/main/java/com/ckkj/defense_device/home/PersonalFragment.kt index 1f734e7..b2d8587 100644 --- a/app/src/main/java/com/ckkj/defense_device/home/PersonalFragment.kt +++ b/app/src/main/java/com/ckkj/defense_device/home/PersonalFragment.kt @@ -16,6 +16,7 @@ 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 @@ -52,7 +53,7 @@ class PersonalFragment : Fragment() { .load("") .circleCrop() .into(binding.ivAvatar) - binding.headerLayout.setOnClickListener { + binding.ivAvatar.setOnClickListener { CoroutineScope(Dispatchers.IO).launch { try { val response = RetrofitNetwork.getNetworkService().getUserInfo() @@ -132,6 +133,10 @@ class PersonalFragment : Fragment() { } dialog.show(childFragmentManager, "logout_dialog") } + binding.ivMenu.setOnClickListener { + val intent = Intent(requireContext(), SettingsMenusActivity::class.java) + startActivity(intent) + } fetchCurrentFamily() return binding.root } diff --git a/app/src/main/java/com/ckkj/defense_device/personal/CancelAccountActivity.kt b/app/src/main/java/com/ckkj/defense_device/personal/CancelAccountActivity.kt new file mode 100644 index 0000000..387fb9d --- /dev/null +++ b/app/src/main/java/com/ckkj/defense_device/personal/CancelAccountActivity.kt @@ -0,0 +1,170 @@ +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.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 { + val verificationCode = binding.etVerificationCode.text.toString() + + if (verificationCode.isEmpty()) { + Toast.makeText(this, R.string.enter_verification_code_pls, Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + if (verificationCode.length != 6) { + Toast.makeText(this, R.string.verification_code_error, Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + cancelAccount(verificationCode) + } + } + + 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() + } + + private fun cancelAccount(verificationCode: String) { + binding.btnCancelAccount.isEnabled = false + + CoroutineScope(Dispatchers.IO).launch { + try { + val response = RetrofitNetwork.getNetworkService().logout() + withContext(Dispatchers.Main) { + binding.btnCancelAccount.isEnabled = true + if (response.code == 200) { + UserinfoUtil.clearUserInfo() + Toast.makeText(this@CancelAccountActivity, getString(R.string.cancel_account), Toast.LENGTH_SHORT).show() + val intent = Intent(this@CancelAccountActivity, LoginActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + startActivity(intent) + finish() + } else { + Toast.makeText(this@CancelAccountActivity, response.msg, Toast.LENGTH_SHORT).show() + } + } + } catch (e: Exception) { + withContext(Dispatchers.Main) { + binding.btnCancelAccount.isEnabled = true + Toast.makeText(this@CancelAccountActivity, R.string.network_error, Toast.LENGTH_SHORT).show() + } + } + } + } + + override fun onDestroy() { + super.onDestroy() + countDownTimer?.cancel() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ckkj/defense_device/personal/SettingsMenusActivity.kt b/app/src/main/java/com/ckkj/defense_device/personal/SettingsMenusActivity.kt new file mode 100644 index 0000000..675a7f7 --- /dev/null +++ b/app/src/main/java/com/ckkj/defense_device/personal/SettingsMenusActivity.kt @@ -0,0 +1,65 @@ +package com.ckkj.defense_device.personal + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import com.ckkj.defense_device.databinding.SettingsMenusActivityBinding +import com.gyf.immersionbar.ImmersionBar + +class SettingsMenusActivity : AppCompatActivity() { + private lateinit var binding: SettingsMenusActivityBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + binding = SettingsMenusActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + + ImmersionBar.with(this) + .statusBarDarkFont(true) + .titleBar(binding.topBar) + .init() + + + val versionName = packageManager.getPackageInfo(packageName, 0).versionName + binding.tvVersion.text = versionName + + setupClickListeners() + } + + private fun setupClickListeners() { + // 返回按钮 + binding.ivBack.setOnClickListener { + finish() + } + + // 检查更新 + binding.itemCheckUpdate.setOnClickListener { + Toast.makeText(this, "正在检查更新...", Toast.LENGTH_SHORT).show() + } + + // 清除缓存 + binding.itemClearCache.setOnClickListener { + Toast.makeText(this, "缓存已清除", Toast.LENGTH_SHORT).show() + binding.tvCacheSize.text = "0 KB" + } + + // 用户协议 + binding.itemUserAgreement.setOnClickListener { + + } + + // 隐私政策 + binding.itemPrivacyPolicy.setOnClickListener { + + } + + // 注销账号 + binding.itemLogout.setOnClickListener { + val intent = Intent(this, CancelAccountActivity::class.java) + startActivity(intent) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_selector_red.xml b/app/src/main/res/drawable/btn_selector_red.xml new file mode 100644 index 0000000..3e61e64 --- /dev/null +++ b/app/src/main/res/drawable/btn_selector_red.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_warning.xml b/app/src/main/res/drawable/ic_warning.xml new file mode 100644 index 0000000..e4dcee8 --- /dev/null +++ b/app/src/main/res/drawable/ic_warning.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/layout/cancel_account_activity.xml b/app/src/main/res/layout/cancel_account_activity.xml new file mode 100644 index 0000000..2e30a00 --- /dev/null +++ b/app/src/main/res/layout/cancel_account_activity.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/personal_fragment.xml b/app/src/main/res/layout/personal_fragment.xml index 676bb63..3712771 100644 --- a/app/src/main/res/layout/personal_fragment.xml +++ b/app/src/main/res/layout/personal_fragment.xml @@ -70,6 +70,15 @@ android:textColor="#666666" android:textSize="16dp" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 7241a66..d2cc5b8 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -501,4 +501,14 @@ 密码修改成功 密码修改失败 更新失败 + + 检查更新 + 清除缓存 + 注销账号 + 如有问题,请联系我们: + + 注销后无法恢复,请谨慎操作 + 当前账号 + 注销 + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 7b7175b..55968a3 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -23,4 +23,12 @@ #EBDCED #CAF4FF + + #09C2BD + #333333 + #666666 + #F8F8F8 + #CCCCCC + #E34D59 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9d8ec4d..99464fe 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,9 +31,7 @@ Confirm Password Password must be 6–20 characters I have read and agree to the - User Agreement and - Privacy Policy Registration Successful Registering... Your account has been created successfully @@ -507,4 +505,17 @@ Password updated successfully Failed to update password Update failed + + + Check for Updates + Clear Cache + Cancel Account + If you have any questions, please contact us: + User Agreement + Privacy Policy + + + Account data cannot be restored after cancellation, please proceed with caution + Current Account + Cancel \ No newline at end of file