diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 2e2e2e0..7586961 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -6,7 +6,8 @@ "Bash(find /d/Test/MyApplication/app/src/main/assets -name \"vocab.txt\" -exec wc -l {} ;)", "WebSearch", "Bash(grep:*)", - "Bash(find:*)" + "Bash(find:*)", + "Bash(ls:*)" ] } } diff --git a/app/src/main/java/com/example/myapplication/MainActivity.kt b/app/src/main/java/com/example/myapplication/MainActivity.kt index 09ea8cd..fabbe0a 100644 --- a/app/src/main/java/com/example/myapplication/MainActivity.kt +++ b/app/src/main/java/com/example/myapplication/MainActivity.kt @@ -235,7 +235,9 @@ class MainActivity : AppCompatActivity() { bottomNav.post { bottomNav.selectedItemId = R.id.home_graph - openGlobal(R.id.loginFragment) // ✅ 退出登录后立刻打开登录页 + // ✅ clearBackStack=true:清空全局回退栈(如 PersonalSettings 等残留页面), + // 避免用户从登录页返回时全局栈未清空导致底部 Tab 栏消失 + openGlobal(R.id.loginFragment, clearBackStack = true) } } diff --git a/app/src/main/java/com/example/myapplication/MyInputMethodService.kt b/app/src/main/java/com/example/myapplication/MyInputMethodService.kt index c78a902..6790602 100644 --- a/app/src/main/java/com/example/myapplication/MyInputMethodService.kt +++ b/app/src/main/java/com/example/myapplication/MyInputMethodService.kt @@ -769,6 +769,9 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment { aiKeyboard = null emojiKeyboard = null + // 深色模式切换时更新主题,下次键盘创建时使用新主题 + ThemeManager.onSystemConfigurationChanged(this) + super.onConfigurationChanged(newConfig) } diff --git a/app/src/main/java/com/example/myapplication/network/ApiService.kt b/app/src/main/java/com/example/myapplication/network/ApiService.kt index b838bdf..b8d2669 100644 --- a/app/src/main/java/com/example/myapplication/network/ApiService.kt +++ b/app/src/main/java/com/example/myapplication/network/ApiService.kt @@ -32,6 +32,17 @@ interface ApiService { suspend fun logout( ): ApiResponse + //注销账户 + @POST("user/cancelAccount") + suspend fun cancelAccount( + ): ApiResponse + + //按 locale 查询提示信息 + @GET("keyboardWarningMessage/byLocale") + suspend fun keyboardWarningMessage( + @Query("locale") id: String + ): ApiResponse + //发送验证邮件 @POST("user/sendVerifyMail") suspend fun sendVerifyCode( diff --git a/app/src/main/java/com/example/myapplication/network/Models.kt b/app/src/main/java/com/example/myapplication/network/Models.kt index 797149c..629993b 100644 --- a/app/src/main/java/com/example/myapplication/network/Models.kt +++ b/app/src/main/java/com/example/myapplication/network/Models.kt @@ -54,6 +54,12 @@ data class VerifyCodeRequest( val mailAddress: String, val verifyCode: String, ) +//注销账户 +data class keyboardWarningMessageResponse( + val locale: String, + val content: String, + val updatedAt: String, +) //重置密码 data class ResetPasswordRequest( diff --git a/app/src/main/java/com/example/myapplication/theme/ThemeManager.kt b/app/src/main/java/com/example/myapplication/theme/ThemeManager.kt index 1b66974..cc59ee5 100644 --- a/app/src/main/java/com/example/myapplication/theme/ThemeManager.kt +++ b/app/src/main/java/com/example/myapplication/theme/ThemeManager.kt @@ -128,17 +128,24 @@ object ThemeManager { } fun init(context: Context) { + android.util.Log.d("1314520-ThemeManager", "init: currentThemeName=$currentThemeName") if (currentThemeName != null) return val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) isManualTheme = prefs.getBoolean(KEY_IS_MANUAL_THEME, false) - + val savedTheme = prefs.getString(KEY_CURRENT_THEME, "default") ?: "default" + + // 默认主题不应视为手动模式,修正历史脏数据 + if (isManualTheme && (savedTheme == "default" || savedTheme == "default_darkness")) { + isManualTheme = false + prefs.edit().putBoolean(KEY_IS_MANUAL_THEME, false).apply() + } + if (!isManualTheme) { updateThemeForSystemMode(context) context.registerSystemDarkModeListener() } else { - val name = prefs.getString(KEY_CURRENT_THEME, "default") ?: "default" - setCurrentTheme(context, name, true) + setCurrentTheme(context, savedTheme, true) } } @@ -147,6 +154,23 @@ object ThemeManager { setCurrentTheme(context, defaultTheme, false) } + /** + * 系统配置变更时调用,非手动主题模式下自动切换深色/浅色主题 + */ + fun onSystemConfigurationChanged(context: Context) { + val dark = isDarkMode(context) + // 默认主题始终跟随系统深色模式 + if (isManualTheme && (currentThemeName == "default" || currentThemeName == "default_darkness")) { + isManualTheme = false + context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .edit().putBoolean(KEY_IS_MANUAL_THEME, false).apply() + } + android.util.Log.d("1314520-ThemeManager", "onSystemConfigurationChanged: isManualTheme=$isManualTheme, isDark=$dark, currentTheme=$currentThemeName") + if (!isManualTheme) { + updateThemeForSystemMode(context) + } + } + /** * 切换当前主题: * - 更新 currentThemeName @@ -154,6 +178,7 @@ object ThemeManager { * - 重新加载该主题的全部图片到缓存 */ fun setCurrentTheme(context: Context, themeName: String, isManual: Boolean = true) { + android.util.Log.d("1314520-ThemeManager", "setCurrentTheme: themeName=$themeName, isManual=$isManual, old=$currentThemeName") currentThemeName = themeName isManualTheme = isManual @@ -163,9 +188,11 @@ object ThemeManager { .putBoolean(KEY_IS_MANUAL_THEME, isManual) .apply() - // 如果是手动选择主题,取消系统模式监听 + // 如果是手动选择主题,取消系统模式监听;否则重新注册 if (isManual) { context.unregisterSystemDarkModeListener() + } else { + context.registerSystemDarkModeListener() } val newFilePathCache: MutableMap = ConcurrentHashMap() diff --git a/app/src/main/java/com/example/myapplication/ui/mine/CancelAccountDialogFragment.kt b/app/src/main/java/com/example/myapplication/ui/mine/CancelAccountDialogFragment.kt new file mode 100644 index 0000000..18636da --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/mine/CancelAccountDialogFragment.kt @@ -0,0 +1,39 @@ +package com.example.myapplication.ui.mine + +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.DialogFragment +import com.example.myapplication.R + +class CancelAccountDialogFragment( + private val onConfirm: () -> Unit +) : DialogFragment() { + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = Dialog(requireContext()) + dialog.setContentView(R.layout.dialog_cancel_account) + dialog.setCancelable(true) + + dialog.window?.apply { + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + + dialog.findViewById(R.id.btn_cancel).setOnClickListener { + dismiss() + } + dialog.findViewById(R.id.btn_confirm).setOnClickListener { + dismiss() + onConfirm() + } + + return dialog + } +} diff --git a/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt b/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt index 5bbca75..52fe929 100644 --- a/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt +++ b/app/src/main/java/com/example/myapplication/ui/mine/MineFragment.kt @@ -46,7 +46,6 @@ class MineFragment : Fragment() { private lateinit var nickname: TextView private lateinit var vipIcon: ImageView private lateinit var time: TextView - private lateinit var logout: TextView private lateinit var avatar: CircleImageView private lateinit var share: LinearLayout private lateinit var loadingOverlay: LoadingOverlay @@ -76,7 +75,6 @@ class MineFragment : Fragment() { nickname = view.findViewById(R.id.nickname) vipIcon = view.findViewById(R.id.vip_icon) time = view.findViewById(R.id.time) - logout = view.findViewById(R.id.logout) avatar = view.findViewById(R.id.avatar) share = view.findViewById(R.id.click_Share) loadingOverlay = LoadingOverlay.attach(view.findViewById(R.id.rootCoordinator)) @@ -123,17 +121,6 @@ class MineFragment : Fragment() { } } - logout.setOnClickListener { - LogoutDialogFragment { doLogout() } - .show(parentFragmentManager, "logout_dialog") - - BehaviorReporter.report( - isNewUser = false, - "page_id" to "person_info", - "element_id" to "logout_btn", - ) - } - view.findViewById(R.id.imgLeft).setOnClickListener { // 使用事件总线打开充值页面 AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.rechargeFragment)) @@ -321,36 +308,6 @@ class MineFragment : Fragment() { } } - private fun doLogout() { - viewLifecycleOwner.lifecycleScope.launch { - try { - val response = RetrofitClient.apiService.logout() - if (!isAdded) return@launch - - if (response.code == 0) { - EncryptedSharedPreferencesUtil.remove(requireContext(), "Personal_information") - EncryptedSharedPreferencesUtil.remove(requireContext(), "user") - - // 清空 UI - nickname.text = "" - time.text = "" - renderVip(false, null) - Glide.with(requireContext()) - .load(R.drawable.default_avatar) - .placeholder(R.drawable.component_loading) - .error(R.drawable.no_search_result) - .into(avatar) - - // 触发登出事件,让MainActivity打开登录页面 - AuthEventBus.emit(AuthEvent.Logout(returnTabTag = "tab_mine")) - } else { - Log.e(TAG, "logout fail code=${response.code}") - } - } catch (e: Exception) { - Log.e(TAG, "logout exception", e) - } - } - } private fun renderVip(isVip: Boolean?, vipLevel: Int?) { diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/CancelAccount.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/CancelAccount.kt new file mode 100644 index 0000000..731a4ab --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/CancelAccount.kt @@ -0,0 +1,109 @@ +package com.example.myapplication.ui.mine.myotherpages + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.webkit.WebView +import android.widget.FrameLayout +import android.widget.ProgressBar +import android.widget.TextView +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import com.example.myapplication.R +import com.example.myapplication.network.AuthEvent +import com.example.myapplication.network.AuthEventBus +import com.example.myapplication.network.RetrofitClient +import com.example.myapplication.ui.mine.CancelAccountDialogFragment +import com.example.myapplication.utils.EncryptedSharedPreferencesUtil +import kotlinx.coroutines.launch + +class CancelAccount : Fragment() { + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.cancel_account, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + view.findViewById(R.id.iv_close).setOnClickListener { + AuthEventBus.emit(AuthEvent.UserUpdated) + requireActivity().onBackPressedDispatcher.onBackPressed() + } + + view.findViewById(R.id.btn_delete).setOnClickListener { + CancelAccountDialogFragment { doCancelAccount() } + .show(parentFragmentManager, "cancel_account_dialog") + } + + loadWarningMessage(view) + } + + private fun loadWarningMessage(view: View) { + val progressBar = view.findViewById(R.id.progressBar) + val webView = view.findViewById(R.id.webView) + val tvEmpty = view.findViewById(R.id.tvEmpty) + + val locale = resources.configuration.locales[0] + val apiLocale = when (locale.language) { + "zh" -> "zh-CN" + else -> "en-US" + } + + viewLifecycleOwner.lifecycleScope.launch { + try { + val response = RetrofitClient.apiService.keyboardWarningMessage(apiLocale) + if (!isAdded) return@launch + + val content = response.data?.content + if (response.code == 0 && !content.isNullOrBlank()) { + progressBar.visibility = View.GONE + webView.visibility = View.VISIBLE + webView.isNestedScrollingEnabled = false + webView.setBackgroundColor(0x00000000) + webView.loadDataWithBaseURL( + null, + content, + "text/html; charset=UTF-8", + "UTF-8", + null + ) + } else { + progressBar.visibility = View.GONE + tvEmpty.visibility = View.VISIBLE + } + } catch (e: Exception) { + Log.e("CancelAccount", "loadWarningMessage exception", e) + if (!isAdded) return@launch + progressBar.visibility = View.GONE + tvEmpty.visibility = View.VISIBLE + } + } + } + + private fun doCancelAccount() { + viewLifecycleOwner.lifecycleScope.launch { + try { + val response = RetrofitClient.apiService.cancelAccount() + if (!isAdded) return@launch + + if (response.code == 0) { + EncryptedSharedPreferencesUtil.remove(requireContext(), "Personal_information") + EncryptedSharedPreferencesUtil.remove(requireContext(), "user") + + AuthEventBus.emit(AuthEvent.Logout(returnTabTag = "tab_mine")) + } else { + Log.e("CancelAccount", "cancelAccount fail code=${response.code}") + } + } catch (e: Exception) { + Log.e("CancelAccount", "cancelAccount exception", e) + } + } + } +} diff --git a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt index 80701b5..2f889c9 100644 --- a/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt +++ b/app/src/main/java/com/example/myapplication/ui/mine/myotherpages/PersonalSettings.kt @@ -31,6 +31,8 @@ import com.example.myapplication.network.RetrofitClient import com.example.myapplication.network.User import com.example.myapplication.network.updateInfoRequest import com.example.myapplication.ui.common.LoadingOverlay +import com.example.myapplication.ui.mine.LogoutDialogFragment +import com.example.myapplication.utils.EncryptedSharedPreferencesUtil import com.google.android.material.bottomsheet.BottomSheetDialogFragment import de.hdodenhof.circleimageview.CircleImageView import kotlinx.coroutines.launch @@ -45,6 +47,10 @@ import android.util.Log class PersonalSettings : BottomSheetDialogFragment() { + companion object { + private const val TAG = "PersonalSettings" + } + private var user: User? = null private lateinit var avatar: CircleImageView @@ -178,8 +184,25 @@ class PersonalSettings : BottomSheetDialogFragment() { Toast.makeText(requireContext(), getString(R.string.personal_settings_copy), Toast.LENGTH_SHORT).show() } + //注销账号 + view.findViewById(R.id.row_cancel_account).setOnClickListener { + AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.CancelAccount)) + } + // ===================== load & render ===================== + // 退出登录 + view.findViewById(R.id.logout).setOnClickListener { + LogoutDialogFragment { doLogout() } + .show(parentFragmentManager, "logout_dialog") + + BehaviorReporter.report( + isNewUser = false, + "page_id" to "person_info", + "element_id" to "logout_btn", + ) + } + loadUser() } @@ -213,7 +236,7 @@ class PersonalSettings : BottomSheetDialogFragment() { Glide.with(this) .load(u.avatarUrl) .placeholder(R.drawable.component_loading) - .error(R.drawable.no_search_result) + .error(R.drawable.default_avatar) .into(avatar) } @@ -229,11 +252,48 @@ class PersonalSettings : BottomSheetDialogFragment() { cm.setPrimaryClip(ClipData.newPlainText("user_id", text)) } - private suspend fun getUserdata(): ApiResponse? = - runCatching { RetrofitClient.apiService.getUser() }.getOrNull() + private suspend fun getUserdata(): ApiResponse? { + Log.d(TAG, "getUserdata: 开始请求用户数据") + return runCatching { + RetrofitClient.apiService.getUser() + }.onSuccess { + Log.d(TAG, "getUserdata: 请求成功 code=${it.code}, data=${it.data}") + }.onFailure { + Log.e(TAG, "getUserdata: 请求异常", it) + }.getOrNull() + } - private suspend fun setupdateUserInfo(body: updateInfoRequest): ApiResponse? = - runCatching { RetrofitClient.apiService.updateUserInfo(body) }.getOrNull() + private suspend fun setupdateUserInfo(body: updateInfoRequest): ApiResponse? { + Log.d(TAG, "setupdateUserInfo: 开始更新用户信息 body=$body") + return runCatching { + RetrofitClient.apiService.updateUserInfo(body) + }.onSuccess { + Log.d(TAG, "setupdateUserInfo: 更新成功 code=${it.code}, data=${it.data}") + }.onFailure { + Log.e(TAG, "setupdateUserInfo: 更新异常", it) + }.getOrNull() + } + + private fun doLogout() { + viewLifecycleOwner.lifecycleScope.launch { + try { + val response = RetrofitClient.apiService.logout() + if (!isAdded) return@launch + + if (response.code == 0) { + EncryptedSharedPreferencesUtil.remove(requireContext(), "Personal_information") + EncryptedSharedPreferencesUtil.remove(requireContext(), "user") + + dismiss() + AuthEventBus.emit(AuthEvent.Logout(returnTabTag = "tab_mine")) + } else { + Log.e("PersonalSettings", "logout fail code=${response.code}") + } + } catch (e: Exception) { + Log.e("PersonalSettings", "logout exception", e) + } + } + } private val cameraPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission() @@ -285,6 +345,7 @@ class PersonalSettings : BottomSheetDialogFragment() { } private fun handleImageResult(uri: Uri) { + // Log.d(TAG, "handleImageResult: 选择图片 uri=$uri") Glide.with(this) .load(uri) .placeholder(R.drawable.component_loading) @@ -297,6 +358,7 @@ class PersonalSettings : BottomSheetDialogFragment() { } private suspend fun uploadAvatar(uri: Uri) { + // Log.d(TAG, "1314520-uploadAvatar: 开始上传图片 uri=$uri") BehaviorReporter.report( isNewUser = false, "page_id" to "person_info", @@ -315,6 +377,8 @@ class PersonalSettings : BottomSheetDialogFragment() { val fileExtension = if (isPng) ".png" else ".jpg" val mediaType = if (isPng) "image/png" else "image/jpeg" + // Log.d(TAG, "1314520-uploadAvatar: mimeType=$mimeType, isPng=$isPng, fileExtension=$fileExtension") + // Temp file in app-private external files dir (no storage permission needed) val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) @@ -322,9 +386,16 @@ class PersonalSettings : BottomSheetDialogFragment() { // 先读取尺寸信息(inJustDecodeBounds) val boundsOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true } - resolver.openInputStream(uri)?.use { ins -> + val boundsStream = resolver.openInputStream(uri) + if (boundsStream == null) { + Log.e(TAG, "1314520-uploadAvatar: openInputStream 返回 null,无法读取图片") + return + } + boundsStream.use { ins -> BitmapFactory.decodeStream(ins, null, boundsOptions) - } ?: return + } + + // Log.d(TAG, "1314520-uploadAvatar: 图片尺寸 ${boundsOptions.outWidth}x${boundsOptions.outHeight}") // Calculate inSampleSize (粗略控制内存) var inSampleSize = 1 @@ -352,10 +423,13 @@ class PersonalSettings : BottomSheetDialogFragment() { } if (bitmap == null) { + // Log.e(TAG, "1314520-uploadAvatar: bitmap 解码失败,无法生成位图") Toast.makeText(requireContext(),getString(R.string.Pop_up_window_PersonalSettings_3), Toast.LENGTH_SHORT).show() return } + // Log.d(TAG, "1314520-uploadAvatar: bitmap 解码成功 ${bitmap.width}x${bitmap.height}, inSampleSize=$inSampleSize") + // Compress to tempFile // 注意:用 outputStream 反复 compress 时不要在同一个 stream 上 truncate, // 最稳是每次重新打开 stream 写入。 @@ -374,6 +448,7 @@ class PersonalSettings : BottomSheetDialogFragment() { } if (tempFile.length() > maxSizeBytes) { + Log.e(TAG, "1314520-uploadAvatar: 压缩后仍超过5MB, fileSize=${tempFile.length()}") Toast.makeText(requireContext(), getString(R.string.Pop_up_window_PersonalSettings_4), Toast.LENGTH_SHORT).show() bitmap.recycle() tempFile.delete() @@ -383,23 +458,30 @@ class PersonalSettings : BottomSheetDialogFragment() { val requestFile: RequestBody = RequestBody.create(mediaType.toMediaTypeOrNull(), tempFile) val body = MultipartBody.Part.createFormData("file", tempFile.name, requestFile) + // Log.d(TAG, "1314520-uploadAvatar: 准备上传 fileName=${tempFile.name}, fileSize=${tempFile.length()}, mediaType=$mediaType") + val response = RetrofitClient.createFileUploadService() .uploadFile("avatar", body) + + // Log.d("1314520-PersonalSettings", "uploadAvatar: $response") // Clean up bitmap.recycle() tempFile.delete() if (response?.code == 0) { - setupdateUserInfo(updateInfoRequest(avatarUrl = response.data)) + // Log.d(TAG, "1314520-uploadAvatar: 上传成功, avatarUrl=${response.data}, 开始更新用户信息") + val updateResp = setupdateUserInfo(updateInfoRequest(avatarUrl = response.data)) + // Log.d(TAG, "1314520-uploadAvatar: 更新用户信息结果 code=${updateResp?.code}, data=${updateResp?.data}") Toast.makeText(requireContext(), R.string.personal_avatar_updated, Toast.LENGTH_SHORT).show() user = user?.copy(avatarUrl = response.data) } else { + // Log.e(TAG, "1314520-uploadAvatar: 上传失败 code=${response?.code}, message=${response?.message}") Toast.makeText(requireContext(), R.string.personal_upload_failed, Toast.LENGTH_SHORT).show() } } catch (e: Exception) { Toast.makeText(requireContext(), R.string.personal_upload_failed, Toast.LENGTH_SHORT).show() - Log.e("PersonalSettings", "Upload avatar error", e) + Log.e("1314520-PersonalSettings", "Upload avatar error", e) } finally { loadingOverlay.hide() } diff --git a/app/src/main/java/com/example/myapplication/ui/shop/myskin/MySkin.kt b/app/src/main/java/com/example/myapplication/ui/shop/myskin/MySkin.kt index 2cc883d..98e7185 100644 --- a/app/src/main/java/com/example/myapplication/ui/shop/myskin/MySkin.kt +++ b/app/src/main/java/com/example/myapplication/ui/shop/myskin/MySkin.kt @@ -139,7 +139,7 @@ class MySkin : Fragment() { // 如果当前主题是被删除的主题之一,切换到默认主题 val currentTheme = com.example.myapplication.theme.ThemeManager.getCurrentThemeName() if (currentTheme != null && ids.any { it.toString() == currentTheme }) { - com.example.myapplication.theme.ThemeManager.setCurrentTheme(requireContext(), "default") + com.example.myapplication.theme.ThemeManager.setCurrentTheme(requireContext(), "default", false) } adapter.removeByIds(ids.toSet()) diff --git a/app/src/main/res/drawable/bg_black_round_8dp.xml b/app/src/main/res/drawable/bg_black_round_8dp.xml new file mode 100644 index 0000000..908aa1d --- /dev/null +++ b/app/src/main/res/drawable/bg_black_round_8dp.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/layout/cancel_account.xml b/app/src/main/res/layout/cancel_account.xml new file mode 100644 index 0000000..6722feb --- /dev/null +++ b/app/src/main/res/layout/cancel_account.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_cancel_account.xml b/app/src/main/res/layout/dialog_cancel_account.xml new file mode 100644 index 0000000..484b03b --- /dev/null +++ b/app/src/main/res/layout/dialog_cancel_account.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml index 1128adc..56a506b 100644 --- a/app/src/main/res/layout/fragment_mine.xml +++ b/app/src/main/res/layout/fragment_mine.xml @@ -508,18 +508,6 @@ - - + - + + + + + + + + + + + + + + @@ -290,5 +341,21 @@ + + + diff --git a/app/src/main/res/navigation/global_graph.xml b/app/src/main/res/navigation/global_graph.xml index 2fe2e46..b83491f 100644 --- a/app/src/main/res/navigation/global_graph.xml +++ b/app/src/main/res/navigation/global_graph.xml @@ -93,6 +93,13 @@ android:label="Personal Settings" tools:layout="@layout/personal_settings" /> + + + 确认退出登录 您确认退出登录吗? 退出登录 + 确认注销账号 + 注销后账号将被停用,并清除本地登录数据,是否继续? + 确认 个人设置 昵称 @@ -68,6 +71,11 @@ 从相册选择 拍照 上传失败 + 注销账号 + + 注销账户须知 + 注销账号 + 确认注销账户 我的人设 删除人设 diff --git a/app/src/main/res/values/strings_i18n.xml b/app/src/main/res/values/strings_i18n.xml index dc4711c..abbd725 100644 --- a/app/src/main/res/values/strings_i18n.xml +++ b/app/src/main/res/values/strings_i18n.xml @@ -57,6 +57,9 @@ Confirm logging out Are you sure you want to log out? Log out + Confirm account cancellation + After cancellation, your account will be deactivated and local login data will be cleared. Continue? + Confirm @@ -71,6 +74,11 @@ Select from the photo album Take a photo Upload failed + Cancel account + + Cancel Account Notice + Cancel Account + Confirm Cancel Account My keyboard Delete character design