diff --git a/app/build.gradle b/app/build.gradle index c52001f..7201666 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -80,4 +80,6 @@ dependencies { implementation "androidx.camera:camera-view:1.0.0-alpha25" //圆形视图库 implementation 'com.makeramen:roundedimageview:2.3.0' + //完整版引入 + implementation 'io.github.carguo:gsyvideoplayer:13.2.1' } diff --git a/app/src/main/java/com/juhabi/birds/AppConfig.kt b/app/src/main/java/com/juhabi/birds/AppConfig.kt index 8e711b8..b56aaa8 100644 --- a/app/src/main/java/com/juhabi/birds/AppConfig.kt +++ b/app/src/main/java/com/juhabi/birds/AppConfig.kt @@ -12,6 +12,8 @@ object AppConfig { const val SEND_CODE_SCENE_LOGIN = "login" //验证码场景 --重置密码 const val SEND_CODE_SCENE_RESET = "resetPwd" + //验证码场景 --更换手机号 + const val SEND_CODE_SCENE_REBIND = "rebind" @Volatile var userToken: String = "" diff --git a/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt b/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt index 29dc330..e041a30 100644 --- a/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt +++ b/app/src/main/java/com/juhabi/birds/base/BaseActivity.kt @@ -276,7 +276,17 @@ abstract class BaseActivity> : } } dialog.setCanceledOnTouchOutside(touchOutsideCancel) - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 直接设置窗口宽度,确保屏幕左右各保留 50dp,避免 AlertDialog 默认宽度影响根布局 margin。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } @@ -322,7 +332,17 @@ abstract class BaseActivity> : } } dialog.setCanceledOnTouchOutside(touchOutsideCancel) - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 固定窗口宽度,确保输入弹窗左右各保留 50dp。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } @@ -370,7 +390,17 @@ abstract class BaseActivity> : val dialog = AlertDialog.Builder(this).setView(view).create() view.findViewById(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() } view.findViewById(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() } - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } diff --git a/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt b/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt index 41b34fa..b092c14 100644 --- a/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt +++ b/app/src/main/java/com/juhabi/birds/base/BaseFragment.kt @@ -276,7 +276,17 @@ abstract class BaseFragment> : Fragment(), I } } dialog.setCanceledOnTouchOutside(touchOutsideCancel) - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 直接设置窗口宽度,确保屏幕左右各保留 50dp,避免 AlertDialog 默认宽度影响根布局 margin。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } @@ -322,7 +332,17 @@ abstract class BaseFragment> : Fragment(), I } } dialog.setCanceledOnTouchOutside(touchOutsideCancel) - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 固定窗口宽度,确保输入弹窗左右各保留 50dp。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } @@ -368,7 +388,17 @@ abstract class BaseFragment> : Fragment(), I val dialog = AlertDialog.Builder(requireContext()).setView(view).create() view.findViewById(R.id.permissionCancel).setOnClickListener { dialog.dismiss(); onCancel() } view.findViewById(R.id.permissionAllow).setOnClickListener { dialog.dismiss(); onAllow() } - dialog.setOnShowListener { dialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) } + dialog.setOnShowListener { + dialog.window?.apply { + // 动态权限确认弹窗固定左右各 50dp,避免使用 AlertDialog 默认宽度。 + setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT)) + setLayout( + (resources.displayMetrics.widthPixels - + 2 * (50 * resources.displayMetrics.density).toInt()), + WindowManager.LayoutParams.WRAP_CONTENT + ) + } + } dialog.show() } /** 关闭并释放顶部权限提示弹窗。 */ diff --git a/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt index fe274f3..4c4dc95 100644 --- a/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt +++ b/app/src/main/java/com/juhabi/birds/mine/MineFragment.kt @@ -4,6 +4,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import java.io.File +import java.util.Locale import com.juhabi.birds.base.BaseFragment import com.juhabi.birds.mine.model.MineUserInfo import com.juhabi.birds.R @@ -14,6 +16,8 @@ import com.juhabi.birds.mine.mvp.MineContract import com.juhabi.birds.mine.mvp.MineImpl import com.juhabi.birds.mine.mvp.MinePresenter import com.juhabi.birds.util.manager.UserInfoManager +import com.juhabi.birds.AppConfig +import com.juhabi.birds.login.LoginActivity /** * 个人中心 @@ -31,11 +35,13 @@ class MineFragment : BaseFragment(), override fun onResume() { super.onResume() + displayCacheSize() /** 页面恢复显示时重新请求当前用户的个人信息。 */ presenter?.getUserInfo() } override fun initView(savedInstanceState: Bundle?) { + displayCacheSize() listOf( binding.linearXgmm, binding.linearGhsj, @@ -62,6 +68,23 @@ class MineFragment : BaseFragment(), goTo(UpdateUserAccount::class.java, null) } + R.id.linear_qchc -> { + showDialogtext( + "提示", + "清除当前应用缓存?", + "确定", + "取消", + object : OnDialogClick { + override fun onCancelClick() { + } + + override fun onConfirmClick() { + clearCache() + } + } + ) + } + //退出登录 R.id.tv_logout -> { @@ -75,7 +98,8 @@ class MineFragment : BaseFragment(), } override fun onConfirmClick() { - // 退出登录接口后续接入 + // 用户确认后调用服务端退出登录接口。 + presenter?.userLogout() } } ) @@ -108,4 +132,75 @@ class MineFragment : BaseFragment(), override fun showError(message: String) { showMessage(message) } + + override fun onLogoutSuccess(message: String) { + val context = context ?: return + // 服务端退出成功后清除本地账号信息,避免应用重启后恢复旧登录状态。 + UserInfoManager.clearUser(context) + AppConfig.USER_LOGIN = false + showMessage(message) + + // 清空之前的页面栈,防止用户返回到已登录页面。 + goToClearTask(LoginActivity::class.java, null) + } + + /** 查询应用缓存大小,并将结果显示在个人中心页面。 */ + private fun displayCacheSize() { + val context = context ?: return + Thread { + val cacheSize = getFolderSize(context.cacheDir) + val sizeText = formatCacheSize(cacheSize) + activity?.runOnUiThread { + if (isAdded && view != null) { + binding.tvTotalCache.text = sizeText + } + } + }.start() + } + + /** 清理应用缓存,删除完成后重新创建缓存目录并刷新显示。 */ + private fun clearCache() { + val context = context ?: return + Thread { + deleteDir(context.cacheDir) + context.cacheDir.mkdirs() + activity?.runOnUiThread { + if (isAdded && view != null) { + binding.tvTotalCache.text = formatCacheSize(0L) + showMessage("缓存清理成功") + } + } + }.start() + } + + /** 递归计算目录下所有文件的字节数。 */ + private fun getFolderSize(directory: File?): Long { + if (directory == null || !directory.exists()) { + return 0L + } + if (directory.isFile) { + return directory.length() + } + return directory.listFiles()?.sumOf { file -> getFolderSize(file) } ?: 0L + } + + /** 递归删除文件或目录。 */ + private fun deleteDir(file: File?): Boolean { + if (file == null || !file.exists()) { + return true + } + if (file.isDirectory) { + file.listFiles()?.forEach { child -> deleteDir(child) } + } + return file.delete() + } + + /** 大于等于 1 MB 使用 MB,否则使用 KB,保持与旧 Java 页面一致。 */ + private fun formatCacheSize(bytes: Long): String { + return if (bytes >= 1024L * 1024L) { + String.format(Locale.US, "%.2f MB", bytes / (1024.0 * 1024.0)) + } else { + String.format(Locale.US, "%d KB", bytes / 1024L) + } + } } diff --git a/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt b/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt index f251edf..92c7db8 100644 --- a/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt +++ b/app/src/main/java/com/juhabi/birds/mine/UpdateUserAccount.kt @@ -1,6 +1,7 @@ package com.juhabi.birds.mine import android.os.Bundle +import android.os.CountDownTimer import android.view.LayoutInflater import android.view.View import com.gyf.immersionbar.ImmersionBar @@ -14,12 +15,15 @@ import com.juhabi.birds.mine.mvp.MineContract import com.juhabi.birds.mine.mvp.MineImpl import com.juhabi.birds.mine.mvp.MinePresenter import com.juhabi.birds.util.manager.UserInfoManager +import com.juhabi.birds.util.ToastUtil /** * 修改手机号 */ class UpdateUserAccount : BaseActivity(), MineContract.MineView, View.OnClickListener { + private var codeCountDownTimer: CountDownTimer? = null + private var newPhoneCodeCountDownTimer: CountDownTimer? = null override fun createBinding(inflater: LayoutInflater): UpdateUserPhoneLayoutBinding { return UpdateUserPhoneLayoutBinding.inflate(inflater) } @@ -59,22 +63,132 @@ class UpdateUserAccount : BaseActivity{ - + // 新接口根据登录态识别当前手机号,不需要传手机号或验证码场景。 + presenter?.getCurrentPhoneCode() } R.id.tv_confirmAccount ->{ + // 只有填写旧手机号收到的验证码后,才能进入新手机号填写页面。 + val confirmCode = binding.EtConfirmCode.text.toString().trim() + if (confirmCode.isBlank()) { + ToastUtil.showShort(mContext, "验证码不能为空") + return + } + + // 服务端校验通过后,再进入新手机号填写页面。 + presenter?.verifyOldPhone(confirmCode) } R.id.tv_new_phone_getcode ->{ + // 新手机号不能为空,校验通过后使用 rebind 场景发送验证码。 + val newPhone = binding.etNewPhone.text.toString().trim() + if (newPhone.isBlank()) { + ToastUtil.showShort(mContext, "新手机号不能为空") + return + } + presenter?.sendNewPhoneCode(newPhone) } R.id.tv_submit_new_account ->{ + //绑定新的手机号码 + val newPhone = binding.etNewPhone.text.toString().trim() + val code = binding.etNewPhoneCode.text.toString().trim() + if (newPhone.isBlank()) { + ToastUtil.showShort(mContext, "新手机号不能为空") + return + } + if (code.isBlank()) { + ToastUtil.showShort(mContext, "验证码不能为空") + return + } + + // 手机号和验证码校验通过后,提交新手机号换绑请求。 + presenter?.updateBindNewPhone(newPhone, code) } } } override fun showError(message: String) { + hideLoading() + ToastUtil.showShort(mContext, message) + } + + override fun sendCodeSuccess(message: String) { + hideLoading() + ToastUtil.showShort(mContext, message) + codeCountDownTimer?.cancel() + binding.tvConfirmGetcode.apply { + isEnabled = false + isClickable = false + setBackgroundResource(R.drawable.shape_register_code_disabled_bg) + text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS) + } + codeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) { + override fun onTick(millisUntilFinished: Long) { + val secondsRemaining = (millisUntilFinished + 999L) / 1_000L + binding.tvConfirmGetcode.text = getString( + R.string.register_code_countdown, + secondsRemaining + ) + } + + override fun onFinish() { + codeCountDownTimer = null + binding.tvConfirmGetcode.apply { + isEnabled = true + isClickable = true + setBackgroundResource(R.drawable.shape_register_code_bg) + setText(R.string.register_get_code) + } + } + }.start() + } + + override fun onVerifyOldPhoneSuccess(message: String) { + hideLoading() + ToastUtil.showShort(mContext, message) + // 当前手机号验证成功后,隐藏验证区域并展示新手机号输入区域。 + binding.linearConfirmUserAccount.visibility = View.GONE + binding.linearNewUserAccount.visibility = View.VISIBLE + } + + override fun newPhoneCodeSuccess(message: String) { + hideLoading() + ToastUtil.showShort(mContext, message) + newPhoneCodeCountDownTimer?.cancel() + binding.tvNewPhoneGetcode.apply { + isEnabled = false + isClickable = false + setBackgroundResource(R.drawable.shape_register_code_disabled_bg) + text = getString(R.string.register_code_countdown, CODE_COUNTDOWN_SECONDS) + } + newPhoneCodeCountDownTimer = object : CountDownTimer(CODE_COUNTDOWN_MILLIS, 1_000L) { + override fun onTick(millisUntilFinished: Long) { + val secondsRemaining = (millisUntilFinished + 999L) / 1_000L + binding.tvNewPhoneGetcode.text = getString( + R.string.register_code_countdown, + secondsRemaining + ) + } + + override fun onFinish() { + newPhoneCodeCountDownTimer = null + binding.tvNewPhoneGetcode.apply { + isEnabled = true + isClickable = true + setBackgroundResource(R.drawable.shape_register_code_bg) + setText(R.string.register_get_code) + } + } + }.start() + } + + override fun onUpdateBindNewPhoneSuccess(message: String) { + hideLoading() + ToastUtil.showShort(mContext, message) + // 返回个人中心后,MineFragment 会在 onResume 中重新获取最新用户资料。 + finish() } override fun getUserInfoSuccess(userInfo: MineUserInfo) { @@ -83,5 +197,18 @@ class UpdateUserAccount : BaseActivity { @@ -17,6 +20,15 @@ interface MineContract { callback: Callback ) fun modifyPassword(oldPassword: String, newPassword: String, callback: Callback) + fun getCurrentPhoneCode(callback: Callback>) + fun sendPhoneCode(request: JSONObject, callback: Callback>) + fun verifyOldPhone(oldCode: String, callback: Callback>) + fun updateBindNewPhone( + newPhoneNumber: String, + code: String, + callback: Callback> + ) + fun userLogout(callback: Callback>) fun cancel() } @@ -25,5 +37,15 @@ interface MineContract { fun onVersionChecked(versionInfo: AppVersionInfo) /** 修改密码成功回调,非修改密码页面无需处理。 */ fun onModifyPasswordSuccess(message: String) {} + /** 发送验证码成功回调。 */ + fun sendCodeSuccess(message: String) {} + /** 新手机号验证码发送成功回调。 */ + fun newPhoneCodeSuccess(message: String) {} + /** 当前手机号验证成功回调。 */ + fun onVerifyOldPhoneSuccess(message: String) {} + /** 新手机号绑定成功回调。 */ + fun onUpdateBindNewPhoneSuccess(message: String) {} + /** 退出登录成功回调。 */ + fun onLogoutSuccess(message: String) {} } } diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt index 931e0f3..16836a3 100644 --- a/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt +++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MineImpl.kt @@ -11,12 +11,177 @@ import retrofit2.Call import retrofit2.Callback import retrofit2.Response import org.json.JSONObject +import com.juhabi.birds.login.model.SendCodeEntity class MineImpl : MineContract.DataModel { private val mainHandler = Handler(Looper.getMainLooper()) private var userInfoCall: Call>? = null private var versionCall: Call>? = null private var modifyPasswordCall: Call>? = null + private var sendCodeCall: Call>? = null + private var verifyOldPhoneCall: Call>? = null + private var newPhoneCodeCall: Call>? = null + private var updateBindNewPhoneCall: Call>? = null + private var logoutCall: Call>? = null + + override fun getCurrentPhoneCode( + callback: MineContract.Callback> + ) { + sendCodeCall?.cancel() + sendCodeCall = ApiClient.apiService + .getCurrentPhoneCode(NetworkManager.getToken()) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("验证码发送失败") + } + } + + override fun onFailure( + call: Call>, + error: Throwable + ) { + if (!call.isCanceled) { + mainHandler.post { callback.fail(error.message ?: "网络请求失败") } + } + } + }) + } + } + + override fun sendPhoneCode( + request: JSONObject, + callback: MineContract.Callback> + ) { + newPhoneCodeCall?.cancel() + newPhoneCodeCall = ApiClient.apiService + .sendPhoneCode(NetworkManager.getToken(), NetworkManager.toRequestBody(request)) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("验证码发送失败") + } + } + + override fun onFailure( + call: Call>, + error: Throwable + ) { + if (!call.isCanceled) { + mainHandler.post { callback.fail(error.message ?: "网络请求失败") } + } + } + }) + } + } + + override fun verifyOldPhone( + oldCode: String, + callback: MineContract.Callback> + ) { + verifyOldPhoneCall?.cancel() + val requestBody = JSONObject().apply { + put("oldCode", oldCode.trim()) + } + verifyOldPhoneCall = ApiClient.apiService + .verifyOldPhone(NetworkManager.getToken(), NetworkManager.toRequestBody(requestBody)) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("当前手机号验证失败") + } + } + + override fun onFailure(call: Call>, error: Throwable) { + if (!call.isCanceled) { + mainHandler.post { + callback.fail(error.message ?: "网络请求失败") + } + } + } + }) + } + } + + override fun updateBindNewPhone( + newPhoneNumber: String, + code: String, + callback: MineContract.Callback> + ) { + updateBindNewPhoneCall?.cancel() + val requestBody = JSONObject().apply { + put("newPhoneNumber", newPhoneNumber.trim()) + put("code", code.trim()) + } + updateBindNewPhoneCall = ApiClient.apiService + .updateBindNewPhone( + NetworkManager.getToken(), + NetworkManager.toRequestBody(requestBody) + ) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("手机号更换失败") + } + } + + override fun onFailure(call: Call>, error: Throwable) { + if (!call.isCanceled) { + mainHandler.post { + callback.fail(error.message ?: "网络请求失败") + } + } + } + }) + } + } + + override fun userLogout(callback: MineContract.Callback>) { + logoutCall?.cancel() + logoutCall = ApiClient.apiService + .userLogout(NetworkManager.getToken()) + .also { call -> + call.enqueue(object : Callback> { + override fun onResponse( + call: Call>, + response: Response> + ) { + mainHandler.post { + response.body()?.let(callback::success) + ?: callback.fail("退出登录失败") + } + } + + override fun onFailure(call: Call>, error: Throwable) { + if (!call.isCanceled) { + mainHandler.post { + callback.fail(error.message ?: "网络请求失败") + } + } + } + }) + } + } override fun getUserInfo( params: Map, @@ -145,6 +310,16 @@ class MineImpl : MineContract.DataModel { versionCall = null modifyPasswordCall?.cancel() modifyPasswordCall = null + sendCodeCall?.cancel() + sendCodeCall = null + verifyOldPhoneCall?.cancel() + verifyOldPhoneCall = null + newPhoneCodeCall?.cancel() + newPhoneCodeCall = null + updateBindNewPhoneCall?.cancel() + updateBindNewPhoneCall = null + logoutCall?.cancel() + logoutCall = null mainHandler.removeCallbacksAndMessages(null) } } diff --git a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt index b383613..c902a7b 100644 --- a/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt +++ b/app/src/main/java/com/juhabi/birds/mine/mvp/MinePresenter.kt @@ -3,9 +3,149 @@ package com.juhabi.birds.mine.mvp import com.juhabi.birds.base.BasePresenter import com.juhabi.birds.main.model.AppVersionInfo import com.juhabi.birds.mine.model.MineUserInfo +import com.juhabi.birds.network.BaseBean +import com.juhabi.birds.login.model.SendCodeEntity +import org.json.JSONObject class MinePresenter(private val mineImpl: MineImpl) : BasePresenter() { + /** 通过登录态向当前绑定的手机号发送验证码,无需额外请求参数。 */ + fun getCurrentPhoneCode() { + val currentView = view ?: return + currentView.showLoading() + mineImpl.getCurrentPhoneCode(object : MineContract.Callback> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + sendCodeSuccess(data.message.ifBlank { "验证码已发送" }) + } else { + showError(data.message.ifBlank { "验证码发送失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + }) + } + + /** 向新手机号发送 rebind 场景验证码。 */ + fun sendNewPhoneCode(phoneNumber: String) { + val currentView = view ?: return + currentView.showLoading() + val request = JSONObject().apply { + put("phoneNumber", phoneNumber.trim()) + put("scene", com.juhabi.birds.AppConfig.SEND_CODE_SCENE_REBIND) + } + mineImpl.sendPhoneCode(request, object : MineContract.Callback> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + newPhoneCodeSuccess(data.message.ifBlank { "验证码已发送" }) + } else { + showError(data.message.ifBlank { "验证码发送失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + }) + } + + /** 校验当前手机号收到的验证码。 */ + fun verifyOldPhone(oldCode: String) { + val currentView = view ?: return + currentView.showLoading() + mineImpl.verifyOldPhone(oldCode, object : MineContract.Callback> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + onVerifyOldPhoneSuccess( + data.message.ifBlank { "当前手机号验证成功" } + ) + } else { + showError(data.message.ifBlank { "当前手机号验证失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + }) + } + + /** 提交新手机号和验证码,完成手机号换绑。 */ + fun updateBindNewPhone(newPhoneNumber: String, code: String) { + val currentView = view ?: return + currentView.showLoading() + mineImpl.updateBindNewPhone( + newPhoneNumber, + code, + object : MineContract.Callback> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + onUpdateBindNewPhoneSuccess( + data.message.ifBlank { "手机号更换成功" } + ) + } else { + showError(data.message.ifBlank { "手机号更换失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + } + ) + } + + /** 调用服务端退出登录接口,并统一分发成功或失败结果。 */ + fun userLogout() { + val currentView = view ?: return + currentView.showLoading() + mineImpl.userLogout(object : MineContract.Callback> { + override fun success(data: BaseBean) { + view?.apply { + hideLoading() + if (data.code == 0 || data.code == 200) { + onLogoutSuccess(data.message.ifBlank { "退出登录成功" }) + } else { + showError(data.message.ifBlank { "退出登录失败" }) + } + } + } + + override fun fail(message: String) { + view?.apply { + hideLoading() + showError(message) + } + } + }) + } + fun getUserInfo() { val currentView = view ?: return currentView.showLoading() diff --git a/app/src/main/java/com/juhabi/birds/network/ApiService.kt b/app/src/main/java/com/juhabi/birds/network/ApiService.kt index e661577..c2856c8 100644 --- a/app/src/main/java/com/juhabi/birds/network/ApiService.kt +++ b/app/src/main/java/com/juhabi/birds/network/ApiService.kt @@ -23,6 +23,14 @@ interface ApiService { //发送手机验证码 + /** + * 入参 + * { + * "phoneNumber": "15550099523", + * "scene": "场景值" + * } + * + */ @POST("app/auth/sms/code") fun sendPhoneCode(@HeaderMap headers: Map, @Body requestBody: RequestBody): Call> @@ -194,6 +202,48 @@ interface ApiService { fun modifyPassword(@HeaderMap headers: Map, @Body requestBody: RequestBody): Call> + /** + * 验证原手机号--发送短信 + * + * 不需要任何入参, + */ + @POST("app/user/phone/old-code") + fun getCurrentPhoneCode(@HeaderMap headers: Map): Call> + + + /** + * 验证原手机号--验证手机号 + * + * 入参 + *{ + * "oldCode": "123456" + * } + * + */ + @POST("app/user/phone/verify-old") + fun verifyOldPhone(@HeaderMap headers: Map, @Body requestBody: RequestBody): Call> + + + /** + * 更换新的绑定手机号码 + { + "newPhoneNumber": "string", + "code": "string" + } + */ + @PUT("app/user/phone") + fun updateBindNewPhone(@HeaderMap headers: Map, @Body requestBody: RequestBody): Call> + + + /** + * 退出登录 + * + * 不需要任何入参, + */ + @POST("app/auth/logout") + fun userLogout(@HeaderMap headers: Map): Call> + + @GET("app/v1/checkVersion") diff --git a/app/src/main/res/layout/dialog_input_text.xml b/app/src/main/res/layout/dialog_input_text.xml index ef6ccce..efdcca0 100644 --- a/app/src/main/res/layout/dialog_input_text.xml +++ b/app/src/main/res/layout/dialog_input_text.xml @@ -3,7 +3,6 @@ android:id="@+id/contentDialog" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginHorizontal="36dp" android:background="@drawable/bg_confirm_dialog" android:orientation="vertical"> diff --git a/app/src/main/res/layout/include_activity_back_gray_layout.xml b/app/src/main/res/layout/include_activity_back_gray_layout.xml index a9603c9..da0ea52 100644 --- a/app/src/main/res/layout/include_activity_back_gray_layout.xml +++ b/app/src/main/res/layout/include_activity_back_gray_layout.xml @@ -12,7 +12,7 @@ android:layout_width="22dp" android:layout_height="match_parent" android:layout_centerVertical="true" - android:src="@mipmap/img_back_black" + android:src="@mipmap/icon_back_white" android:layout_marginStart="10dp"/> + + diff --git a/app/src/main/res/layout/update_user_phone_layout.xml b/app/src/main/res/layout/update_user_phone_layout.xml index fc55a45..5d4afca 100644 --- a/app/src/main/res/layout/update_user_phone_layout.xml +++ b/app/src/main/res/layout/update_user_phone_layout.xml @@ -23,6 +23,7 @@ android:id="@+id/linear_confirm_user_account" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="20dp" android:orientation="vertical" android:visibility="visible">