diff --git a/app/src/main/java/com/kaixed/kchat/data/LocalDatabase.kt b/app/src/main/java/com/kaixed/kchat/data/LocalDatabase.kt index 3ffa5e5..eda6e63 100644 --- a/app/src/main/java/com/kaixed/kchat/data/LocalDatabase.kt +++ b/app/src/main/java/com/kaixed/kchat/data/LocalDatabase.kt @@ -1,16 +1,11 @@ package com.kaixed.kchat.data -import com.kaixed.kchat.data.local.box.ObjectBox.getBox -import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore import com.kaixed.kchat.data.local.entity.Contact import com.kaixed.kchat.data.local.entity.Contact_ -import com.kaixed.kchat.data.local.entity.Conversation import com.kaixed.kchat.data.local.entity.Conversation_ import com.kaixed.kchat.data.local.entity.Messages import com.kaixed.kchat.data.local.entity.Messages_ import com.kaixed.kchat.data.local.entity.UserInfo -import io.objectbox.Box -import io.objectbox.kotlin.boxFor import io.objectbox.query.QueryBuilder /** @@ -19,13 +14,13 @@ import io.objectbox.query.QueryBuilder */ object LocalDatabase { - private val contactBox: Box by lazy { getBoxStore().boxFor() } + private val contactBox = DataBase.contactBox - private val messagesBox: Box by lazy { getBoxStore().boxFor() } + private val messagesBox = DataBase.messagesBox - private val conversationBox: Box by lazy { getBoxStore().boxFor() } + private val conversationBox = DataBase.conversationBox - private val userInfoBox: Box by lazy { getBoxStore().boxFor() } + private val userInfoBox = DataBase.userInfoBox fun saveUserInfo(userInfo: UserInfo) { userInfoBox.put(userInfo) diff --git a/app/src/main/java/com/kaixed/kchat/data/model/request/UpdatePasswordRequest.kt b/app/src/main/java/com/kaixed/kchat/data/model/request/UpdatePasswordRequest.kt new file mode 100644 index 0000000..52916e7 --- /dev/null +++ b/app/src/main/java/com/kaixed/kchat/data/model/request/UpdatePasswordRequest.kt @@ -0,0 +1,11 @@ +package com.kaixed.kchat.data.model.request + +/** + * @Author: kaixed + * @Date: 2024/12/15 21:27 + */ +data class UpdatePasswordRequest( + val username: String, + val oldPassword: String, + val newPassword: String, +) diff --git a/app/src/main/java/com/kaixed/kchat/data/repository/UserProfileRepository.kt b/app/src/main/java/com/kaixed/kchat/data/repository/UserProfileRepository.kt index 2ade3ba..86f635f 100644 --- a/app/src/main/java/com/kaixed/kchat/data/repository/UserProfileRepository.kt +++ b/app/src/main/java/com/kaixed/kchat/data/repository/UserProfileRepository.kt @@ -3,8 +3,10 @@ package com.kaixed.kchat.data.repository import com.kaixed.kchat.data.local.box.ObjectBox.getBoxStore import com.kaixed.kchat.data.local.entity.UserInfo import com.kaixed.kchat.data.local.entity.UserInfo_ +import com.kaixed.kchat.data.model.request.UpdatePasswordRequest import com.kaixed.kchat.data.model.request.UserRequest import com.kaixed.kchat.data.model.search.SearchUser +import com.kaixed.kchat.network.ApiCall.apiCall import com.kaixed.kchat.network.RetrofitClient import com.kaixed.kchat.utils.Constants.MMKV_USER_SESSION import com.kaixed.kchat.utils.Constants.NICKNAME_KEY @@ -70,6 +72,14 @@ class UserProfileRepository { } } + // 更新密码 + suspend fun updatePassword(updatePasswordRequest: UpdatePasswordRequest): Result { + return apiCall( + apiCall = { userApiService.updatePassword(updatePasswordRequest) }, + errorMessage = "登录失败" + ) + } + private fun updateNickname(newNickname: String) { val userInfoBox = getBoxStore().boxFor(UserInfo::class.java) val user = userInfoBox diff --git a/app/src/main/java/com/kaixed/kchat/network/service/UserApiService.kt b/app/src/main/java/com/kaixed/kchat/network/service/UserApiService.kt index 225ff99..208dfbc 100644 --- a/app/src/main/java/com/kaixed/kchat/network/service/UserApiService.kt +++ b/app/src/main/java/com/kaixed/kchat/network/service/UserApiService.kt @@ -2,6 +2,7 @@ package com.kaixed.kchat.network.service import com.kaixed.kchat.data.local.entity.UserInfo import com.kaixed.kchat.data.model.request.RegisterRequest +import com.kaixed.kchat.data.model.request.UpdatePasswordRequest import com.kaixed.kchat.data.model.request.UserRequest import com.kaixed.kchat.data.model.response.register.Register import com.kaixed.kchat.data.model.response.search.User @@ -70,4 +71,10 @@ interface UserApiService { @Part("username") username: String, @Part file: MultipartBody.Part ): ApiResponse + + // 更改昵称接口 + @POST("users/password") + suspend fun updatePassword( + @Body updatePasswordRequest: UpdatePasswordRequest + ): ApiResponse } diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/setting/AccountSecurityActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/setting/AccountSecurityActivity.kt index b2f97c2..a9eb48d 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/setting/AccountSecurityActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/setting/AccountSecurityActivity.kt @@ -1,8 +1,9 @@ package com.kaixed.kchat.ui.activity.setting +import android.content.Intent import android.os.Bundle import androidx.activity.enableEdgeToEdge -import com.kaixed.kchat.R +import com.kaixed.kchat.data.DataBase import com.kaixed.kchat.databinding.ActivityAccountSecurityBinding import com.kaixed.kchat.ui.base.BaseActivity @@ -15,8 +16,24 @@ class AccountSecurityActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + + setListener() + } + + private fun setListener() { + binding.ciUpdatePassword.setOnClickListener { + startActivity( + Intent(this, UpdatePasswordActivity::class.java) + ) + } } override fun initData() { + val userInfo = DataBase.userInfoBox.query().build().findFirst() + userInfo?.let { + binding.ciAccount.setItemDesc(userInfo.username) + val telephone = userInfo.telephone.take(3) + "******" + userInfo.telephone.takeLast(2) + binding.ciTelephone.setItemDesc(telephone) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/kaixed/kchat/ui/activity/setting/UpdatePasswordActivity.kt b/app/src/main/java/com/kaixed/kchat/ui/activity/setting/UpdatePasswordActivity.kt index d6a0777..4637f49 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/activity/setting/UpdatePasswordActivity.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/activity/setting/UpdatePasswordActivity.kt @@ -1,25 +1,102 @@ package com.kaixed.kchat.ui.activity.setting import android.os.Bundle +import android.text.Editable +import android.text.InputFilter +import android.text.TextWatcher import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import com.kaixed.kchat.R +import androidx.activity.viewModels +import com.kaixed.kchat.data.model.request.UpdatePasswordRequest import com.kaixed.kchat.databinding.ActivityUpdatePasswordBinding import com.kaixed.kchat.ui.base.BaseActivity +import com.kaixed.kchat.ui.widget.LoadingDialogFragment +import com.kaixed.kchat.utils.ConstantsUtil +import com.kaixed.kchat.viewmodel.UserViewModel class UpdatePasswordActivity : BaseActivity() { + private val userViewModel: UserViewModel by viewModels() + + private val loadingDialog by lazy { LoadingDialogFragment.newInstance("更改密码中...") } + override fun inflateBinding(): ActivityUpdatePasswordBinding { return ActivityUpdatePasswordBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + setListener() + observeLiveData() + } + + private fun observeLiveData() { + userViewModel.updatePasswordResult.observe(this) { result -> + result.onSuccess { + toast("修改成功") + finish() + } + result.onFailure { + toast(it.message.toString()) + } + loadingDialog.dismissLoading() + } + } + + private fun setListener() { + binding.titleBar.setOnBtnClickListener { + val oldPassword = binding.cetOldPassword.text.toString() + val newPassword = binding.cetNewPassword.text.toString() + val confirmPassword = binding.cetConfirmPassword.text.toString() + if (confirmPassword != newPassword) { + toast("两次密码不一致,请重新输入") + return@setOnBtnClickListener + } else { + loadingDialog.showLoading(supportFragmentManager) + val updatePassword = UpdatePasswordRequest( + ConstantsUtil.getUsername(), + oldPassword, + newPassword + ) + userViewModel.updatePassword(updatePassword) + } + } + + binding.cetOldPassword.addTextChangedListener(textWatch) + binding.cetNewPassword.addTextChangedListener(textWatch) + binding.cetConfirmPassword.addTextChangedListener(textWatch) + } + + private fun addEdittextFilter() { + val noSpaceFilter = InputFilter { source, _, _, _, _, _ -> + if (source.contains(" ")) { + return@InputFilter "" + } + null + } + binding.cetOldPassword.filters = arrayOf(noSpaceFilter) + binding.cetNewPassword.filters = arrayOf(noSpaceFilter) + binding.cetConfirmPassword.filters = arrayOf(noSpaceFilter) + } override fun initData() { + binding.tvKid.text = ConstantsUtil.getUsername() + addEdittextFilter() + } + private val textWatch = object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + val enable = binding.cetNewPassword.text?.length != 0 && + binding.cetOldPassword.text?.length != 0 && + binding.cetConfirmPassword.text?.length != 0 + binding.titleBar.setBtnEnable(enable) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/kaixed/kchat/ui/widget/CustomEditText.kt b/app/src/main/java/com/kaixed/kchat/ui/widget/CustomEditText.kt index 2519ac9..ef1be62 100644 --- a/app/src/main/java/com/kaixed/kchat/ui/widget/CustomEditText.kt +++ b/app/src/main/java/com/kaixed/kchat/ui/widget/CustomEditText.kt @@ -1,24 +1,87 @@ package com.kaixed.kchat.ui.widget import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint import android.os.Build import android.util.AttributeSet import androidx.annotation.RequiresApi import androidx.appcompat.widget.AppCompatEditText import androidx.core.content.ContextCompat import com.kaixed.kchat.R +import com.kaixed.kchat.utils.DensityUtil.dp2px /** - * @Author: kaixed - * @Date: 2024/12/12 10:09 + * 自定义 EditText 控件,用于绘制下划线。 */ @RequiresApi(Build.VERSION_CODES.Q) class CustomEditText @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, - defStyleAttr: Int = androidx.appcompat.R.attr.editTextStyle -) : AppCompatEditText(context, attrs, defStyleAttr) { + defStyle: Int = 0 +) : AppCompatEditText(context, attrs, defStyle) { + + // 焦点时的下划线颜色 + private val focusedLineColor: Int by lazy { ContextCompat.getColor(context, R.color.green) } + + // 无焦点时的下划线颜色 + private val normalLineColor: Int by lazy { Color.parseColor("#E0E0E0") } + + // 下划线高度 + private val lineHeight: Float by lazy { dp2px(1).toFloat() } + + // 画笔,用于绘制下划线 + private var paint: Paint = Paint().apply { + style = Paint.Style.STROKE + strokeWidth = lineHeight + color = normalLineColor // 初始颜色为无焦点时的颜色 + } + init { + // 确保背景设置为空以自定义下划线 + background = null + + // 设置焦点状态的参数,确保可以获得焦点 + isFocusable = true + isFocusableInTouchMode = true + textCursorDrawable = ContextCompat.getDrawable(context, R.drawable.cursor) } -} \ No newline at end of file + + /** + * 重写 onDraw() 方法绘制底部下划线 + */ + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + // 绘制底部的下划线 + val startX = 0f + val startY = height.toFloat() // 在底部绘制下划线,距底部5dp + val endX = width.toFloat() + val endY = height.toFloat() + + canvas.drawLine(startX, startY, endX, endY, paint) + } + + /** + * 焦点变化时更新下划线的颜色 + */ + override fun onFocusChanged( + focused: Boolean, + direction: Int, + previouslyFocusedRect: android.graphics.Rect? + ) { + super.onFocusChanged(focused, direction, previouslyFocusedRect) + + // 根据焦点状态更新下划线颜色 + paint.color = if (focused) { + focusedLineColor + } else { + normalLineColor + } + + // 强制重绘 + invalidate() + } +} diff --git a/app/src/main/java/com/kaixed/kchat/viewmodel/UserViewModel.kt b/app/src/main/java/com/kaixed/kchat/viewmodel/UserViewModel.kt index 7d2b9ff..08c18c5 100644 --- a/app/src/main/java/com/kaixed/kchat/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/kaixed/kchat/viewmodel/UserViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.kaixed.kchat.data.local.entity.UserInfo import com.kaixed.kchat.data.model.request.RegisterRequest +import com.kaixed.kchat.data.model.request.UpdatePasswordRequest import com.kaixed.kchat.data.model.request.UserRequest import com.kaixed.kchat.data.model.response.register.Register import com.kaixed.kchat.data.model.response.search.User @@ -27,6 +28,17 @@ class UserViewModel : ViewModel() { private val _loginResult = MutableLiveData>() val loginResult: LiveData> = _loginResult + private val _updatePasswordResult = MutableLiveData>() + val updatePasswordResult: LiveData> = _updatePasswordResult + + // 修改密码 + fun updatePassword(updatePasswordRequest: UpdatePasswordRequest) { + viewModelScope.launch { + val result = userProfileRepo.updatePassword(updatePasswordRequest) + _updatePasswordResult.postValue(result) + } + } + fun loginByUsername(username: String, password: String) { viewModelScope.launch { val result = userAuthRepo.loginByUsername(username, password) diff --git a/app/src/main/res/layout/activity_account_security.xml b/app/src/main/res/layout/activity_account_security.xml index c2bf11c..74abdfc 100644 --- a/app/src/main/res/layout/activity_account_security.xml +++ b/app/src/main/res/layout/activity_account_security.xml @@ -16,19 +16,22 @@ app:titleName="账号与安全" /> + app:itemName="kid号" /> - + + - + android:layout_height="match_parent" + android:paddingHorizontal="15dp"> + + + + + + + + + + + + + + + + + + + + + android:layout_marginTop="10dp" + android:text="密码必须是8-16位的英文字母、数字、字符组合(不能是纯数字)" + android:textColor="@color/black" + android:textSize="13sp" + app:layout_constraintTop_toBottomOf="@id/cet_confirm_password" /> - - +