This commit is contained in:
pengxiaolong
2026-03-02 13:01:54 +08:00
parent c018243116
commit 3a65b3f43d
19 changed files with 582 additions and 73 deletions

View File

@@ -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:*)"
]
}
}

View File

@@ -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)
}
}

View File

@@ -769,6 +769,9 @@ class MyInputMethodService : InputMethodService(), KeyboardEnvironment {
aiKeyboard = null
emojiKeyboard = null
// 深色模式切换时更新主题,下次键盘创建时使用新主题
ThemeManager.onSystemConfigurationChanged(this)
super.onConfigurationChanged(newConfig)
}

View File

@@ -32,6 +32,17 @@ interface ApiService {
suspend fun logout(
): ApiResponse<Boolean>
//注销账户
@POST("user/cancelAccount")
suspend fun cancelAccount(
): ApiResponse<Boolean>
//按 locale 查询提示信息
@GET("keyboardWarningMessage/byLocale")
suspend fun keyboardWarningMessage(
@Query("locale") id: String
): ApiResponse<keyboardWarningMessageResponse>
//发送验证邮件
@POST("user/sendVerifyMail")
suspend fun sendVerifyCode(

View File

@@ -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(

View File

@@ -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<String, File> = ConcurrentHashMap()

View File

@@ -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<View>(R.id.btn_cancel).setOnClickListener {
dismiss()
}
dialog.findViewById<View>(R.id.btn_confirm).setOnClickListener {
dismiss()
onConfirm()
}
return dialog
}
}

View File

@@ -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<ImageView>(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?) {

View File

@@ -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<FrameLayout>(R.id.iv_close).setOnClickListener {
AuthEventBus.emit(AuthEvent.UserUpdated)
requireActivity().onBackPressedDispatcher.onBackPressed()
}
view.findViewById<TextView>(R.id.btn_delete).setOnClickListener {
CancelAccountDialogFragment { doCancelAccount() }
.show(parentFragmentManager, "cancel_account_dialog")
}
loadWarningMessage(view)
}
private fun loadWarningMessage(view: View) {
val progressBar = view.findViewById<ProgressBar>(R.id.progressBar)
val webView = view.findViewById<WebView>(R.id.webView)
val tvEmpty = view.findViewById<TextView>(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)
}
}
}
}

View File

@@ -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<View>(R.id.row_cancel_account).setOnClickListener {
AuthEventBus.emit(AuthEvent.OpenGlobalPage(R.id.CancelAccount))
}
// ===================== load & render =====================
// 退出登录
view.findViewById<TextView>(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<User>? =
runCatching { RetrofitClient.apiService.getUser() }.getOrNull()
private suspend fun getUserdata(): ApiResponse<User>? {
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<Boolean>? =
runCatching { RetrofitClient.apiService.updateUserInfo(body) }.getOrNull()
private suspend fun setupdateUserInfo(body: updateInfoRequest): ApiResponse<Boolean>? {
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()
}

View File

@@ -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())

View File

@@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FF0000" />
<corners android:radius="@dimen/sw_8dp" />
</shape>

View File

@@ -0,0 +1,133 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout
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/rootCoordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F6F7FB"
tools:context=".ui.mine.myotherpages.CancelAccount">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 标题和返回(固定顶部) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="@dimen/sw_16dp"
android:paddingEnd="@dimen/sw_16dp"
android:paddingTop="@dimen/sw_16dp">
<!-- 返回按钮 -->
<FrameLayout
android:id="@+id/iv_close"
android:layout_width="@dimen/sw_46dp"
android:layout_height="@dimen/sw_46dp">
<ImageView
android:layout_width="@dimen/sw_13dp"
android:layout_height="@dimen/sw_13dp"
android:layout_gravity="center"
android:src="@drawable/more_icons"
android:rotation="180"
android:scaleType="fitCenter" />
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="@dimen/sw_49dp"
android:gravity="center"
android:textStyle="bold"
android:text="@string/cancel_account_title"
android:textColor="#1B1F1A"
android:textSize="@dimen/sw_16sp" />
</LinearLayout>
<!-- 提示信息 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="@dimen/sw_40dp"
android:textStyle="bold"
android:paddingStart="@dimen/sw_16dp"
android:paddingEnd="@dimen/sw_16dp"
android:gravity="center_vertical"
android:text="@string/cancel_account_instruction"
android:textColor="#1B1F1A"
android:textSize="@dimen/sw_16sp" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:paddingStart="@dimen/sw_16dp"
android:paddingEnd="@dimen/sw_16dp"
android:orientation="vertical">
<!-- 内容 -->
<FrameLayout
android:id="@+id/click_Notice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="@dimen/sw_64dp"
android:layout_marginTop="@dimen/sw_20dp">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/sw_16dp"
android:layout_marginBottom="@dimen/sw_16dp" />
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="@+id/tvEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="@dimen/sw_16dp"
android:layout_marginBottom="@dimen/sw_16dp"
android:text="@string/search_not_data"
android:textColor="#999999"
android:textSize="@dimen/sw_14sp"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<!-- 删除按钮,固定在页面底部 -->
<TextView
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="@dimen/sw_50dp"
android:layout_marginStart="@dimen/sw_16dp"
android:layout_marginEnd="@dimen/sw_16dp"
android:layout_marginTop="@dimen/sw_10dp"
android:layout_marginBottom="@dimen/sw_20dp"
android:gravity="center"
android:text="@string/cancel_account_delete"
android:textColor="#FFFFFF"
android:textSize="@dimen/sw_16sp"
android:textStyle="bold"
android:background="@drawable/bg_black_round_8dp" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,54 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="@dimen/sw_24dp"
android:background="@drawable/bg_dialog_round"
android:layout_marginStart="@dimen/sw_24dp"
android:layout_marginEnd="@dimen/sw_24dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- 标题 -->
<TextView
android:text="@string/cancel_account_confirm_title"
android:textSize="@dimen/sw_18sp"
android:textStyle="bold"
android:textColor="#222222"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- 描述 -->
<TextView
android:layout_marginTop="@dimen/sw_12dp"
android:text="@string/cancel_account_confirm_msg"
android:textSize="@dimen/sw_14sp"
android:textColor="#666666"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- 按钮区 -->
<LinearLayout
android:layout_marginTop="@dimen/sw_24dp"
android:orientation="horizontal"
android:gravity="end"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/btn_cancel"
android:text="@string/cancel"
android:textSize="@dimen/sw_14sp"
android:textColor="#666666"
android:padding="@dimen/sw_12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/btn_confirm"
android:text="@string/cancel_account_confirm_btn"
android:textSize="@dimen/sw_14sp"
android:textColor="#F44336"
android:padding="@dimen/sw_12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>

View File

@@ -508,18 +508,6 @@
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/logout"
android:layout_width="match_parent"
android:layout_height="@dimen/sw_63dp"
android:layout_marginTop="@dimen/sw_20dp"
android:layout_marginBottom="@dimen/sw_20dp"
android:gravity="center"
android:text="@string/mine_logout"
android:textColor="#FF0000"
android:textSize="@dimen/sw_16sp"
android:textStyle="bold"
android:background="@drawable/settings"/>
<TextView
android:layout_width="match_parent"

View File

@@ -115,7 +115,7 @@
android:layout_marginTop="@dimen/sw_24dp"
android:background="@drawable/settings"
android:orientation="vertical">
<!-- Nickname -->
<!-- 昵称 -->
<LinearLayout
android:id="@+id/row_nickname"
android:layout_width="match_parent"
@@ -159,7 +159,7 @@
</LinearLayout>
</LinearLayout>
<!-- Gender -->
<!-- 性别 -->
<LinearLayout
android:id="@+id/row_gender"
android:layout_width="match_parent"
@@ -247,6 +247,57 @@
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/sw_24dp"
android:background="@drawable/settings"
android:orientation="vertical">
<!-- 注销账号 -->
<LinearLayout
android:id="@+id/row_cancel_account"
android:layout_width="match_parent"
android:layout_height="@dimen/sw_64dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/sw_16dp"
android:text="@string/personal_settings_cancel_account"
android:textColor="#1B1F1A"
android:textStyle="bold"
android:textSize="@dimen/sw_16sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/sw_10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_userid_value"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text=""
android:gravity="end"
android:textColor="#1B1F1A"
android:textStyle="bold"
android:layout_weight="1"
android:textSize="@dimen/sw_16sp" />
<ImageView
android:layout_width="@dimen/sw_9dp"
android:layout_height="@dimen/sw_13dp"
android:tint="#AFAFAF"
android:layout_marginStart="@dimen/sw_12dp"
android:layout_marginEnd="@dimen/sw_16dp"
android:src="@drawable/more_icons" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
@@ -290,5 +341,21 @@
</FrameLayout>
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- 退出登录按钮,固定在页面底部 -->
<TextView
android:id="@+id/logout"
android:layout_width="match_parent"
android:layout_height="@dimen/sw_63dp"
android:layout_marginStart="@dimen/sw_16dp"
android:layout_marginEnd="@dimen/sw_16dp"
android:layout_marginTop="@dimen/sw_10dp"
android:layout_marginBottom="@dimen/sw_20dp"
android:gravity="center"
android:text="@string/mine_logout"
android:textColor="#FF0000"
android:textSize="@dimen/sw_16sp"
android:textStyle="bold"
android:background="@drawable/settings"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -93,6 +93,13 @@
android:label="Personal Settings"
tools:layout="@layout/personal_settings" />
<!-- 注销账号 -->
<fragment
android:id="@+id/CancelAccount"
android:name="com.example.myapplication.ui.mine.myotherpages.CancelAccount"
android:label="Cancel Account"
tools:layout="@layout/cancel_account" />
<!-- 键盘设置 -->
<fragment
android:id="@+id/MyKeyboard"

View File

@@ -56,6 +56,9 @@
<string name="Logout_confirm_title">确认退出登录</string>
<string name="Logout_confirm_msg">您确认退出登录吗?</string>
<string name="Logout_confirm_btn">退出登录</string>
<string name="cancel_account_confirm_title">确认注销账号</string>
<string name="cancel_account_confirm_msg">注销后账号将被停用,并清除本地登录数据,是否继续?</string>
<string name="cancel_account_confirm_btn">确认</string>
<!-- 个人设置页面 -->
<string name="personal_settings_title">个人设置</string>
<string name="personal_settings_nickname">昵称</string>
@@ -68,6 +71,11 @@
<string name="personal_choose_from_gallery">从相册选择</string>
<string name="personal_take_photo">拍照</string>
<string name="personal_upload_failed">上传失败</string>
<string name="personal_settings_cancel_account">注销账号</string>
<!-- 注销页面 -->
<string name="cancel_account_instruction">注销账户须知</string>
<string name="cancel_account_title">注销账号</string>
<string name="cancel_account_delete">确认注销账户</string>
<!-- 键盘人设 -->
<string name="keyboard_title">我的人设</string>
<string name="keyboard_delete_hint">删除人设</string>

View File

@@ -57,6 +57,9 @@
<string name="Logout_confirm_title">Confirm logging out</string>
<string name="Logout_confirm_msg">Are you sure you want to log out?</string>
<string name="Logout_confirm_btn">Log out</string>
<string name="cancel_account_confirm_title">Confirm account cancellation</string>
<string name="cancel_account_confirm_msg">After cancellation, your account will be deactivated and local login data will be cleared. Continue?</string>
<string name="cancel_account_confirm_btn">Confirm</string>
<!-- 个人设置页面 -->
@@ -71,6 +74,11 @@
<string name="personal_choose_from_gallery">Select from the photo album</string>
<string name="personal_take_photo">Take a photo</string>
<string name="personal_upload_failed">Upload failed</string>
<string name="personal_settings_cancel_account">Cancel account</string>
<!-- 注销页面 -->
<string name="cancel_account_instruction">Cancel Account Notice</string>
<string name="cancel_account_title">Cancel Account</string>
<string name="cancel_account_delete">Confirm Cancel Account</string>
<!-- 键盘人设 -->
<string name="keyboard_title">My keyboard</string>
<string name="keyboard_delete_hint">Delete character design</string>