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