隐私政策,用户协议

This commit is contained in:
fengjignqi
2026-09-23 17:41:17 +08:00
parent 60bacffca5
commit 495e43f017
18 changed files with 548 additions and 46 deletions

View File

@@ -85,6 +85,11 @@
android:name=".mine.UpdateUserAccount"
android:exported="false" />
<activity
android:name=".mine.WebviewActivity"
android:exported="false" />
<provider

View File

@@ -3,6 +3,8 @@ package com.juhabi.birds
object AppConfig {
// const val BASE_URL = "http://47.97.217.123:8081"
const val BASE_URL = "http://192.168.0.212:8081/"
const val USER_AGREEMENT_URL = "http://192.168.0.212:8081/static/agreement/user-agreement.html"
const val PRIVACY_POLICY_URL = "http://192.168.0.212:8081/static/agreement/privacy-policy.html"
const val APP_CLIENT_ID = "app"
const val APP_USER_TYPE = "app_user"
const val CONTENT_LANGUAGE = "zh_CN"

View File

@@ -24,6 +24,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
private var resetByPhone = true
private var isPasswordVisible = false
private var isConfirmPasswordVisible = false
@@ -52,6 +53,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
binding.passwordVisibilityButton,
binding.confirmPasswordVisibilityButton,
binding.confirmButton,
binding.tvChangeLoginType,
).forEach { view ->
view.setOnClickListener(this)
}
@@ -63,13 +65,35 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
finish()
}
R.id.tv_change_login_type ->{
resetByPhone = !resetByPhone
binding.linearCountry.visibility = if (resetByPhone) View.VISIBLE else View.GONE
binding.imgLoginType.setImageResource(
if (resetByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
)
binding.tvChangeLoginType.text = if (resetByPhone) "邮箱" else "手机"
binding.phoneEditText.apply {
text?.clear()
hint = if (resetByPhone) "请输入手机号" else "请输入邮箱"
inputType = if (resetByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
}
}
}
R.id.getCodeButton -> {//获取验证码
val phone = binding.phoneEditText.text.toString().trim()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
val account = binding.phoneEditText.text.toString().trim()
if (account.isBlank()) {
ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
return
}
presenter?.getPhoneSmsCode(phone, AppConfig.SEND_CODE_SCENE_RESET)
if (resetByPhone) {
presenter?.getPhoneSmsCode(account, AppConfig.SEND_CODE_SCENE_RESET)
} else {
presenter?.getEmailSmsCode(account, AppConfig.SEND_CODE_SCENE_RESET)
}
}
R.id.passwordVisibilityButton -> {
@@ -83,13 +107,13 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
}
R.id.confirmButton -> {//修改密码
val phone = binding.phoneEditText.text.toString().trim()
val account = binding.phoneEditText.text.toString().trim()
val code = binding.codeEditText.text.toString().trim()
val password = binding.passwordEditText.text.toString()
val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
if (account.isBlank()) {
ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
return
}
if (code.isBlank()) {
@@ -116,7 +140,11 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
return
}
presenter?.forgetPsdByPhone(phone, code, password)
if (resetByPhone) {
presenter?.forgetPsdByPhone(account, code, password)
} else {
presenter?.forgetPsdByEmail(account, code, password)
}
}
}
}

View File

@@ -74,6 +74,9 @@ class RegisterActivity : BaseActivity<RegisterLayoutBinding, LoginPresenter>(),
R.id.tv_change_login_type ->{
registerByPhone = !registerByPhone
binding.linearCountry.visibility = if (registerByPhone) View.VISIBLE else View.GONE
binding.imgLoginType.setImageResource(
if (registerByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
)
binding.tvChangeLoginType.text = if (registerByPhone) "邮箱" else "手机"
binding.phoneEditText.apply {
text?.clear()

View File

@@ -68,6 +68,14 @@ class MineFragment : BaseFragment<MineCenterLayoutBinding, MinePresenter>(),
goTo(UpdateUserAccount::class.java, null)
}
R.id.linear_ysxy -> {
openAgreementPage(WebviewActivity.PAGE_PRIVACY, AppConfig.PRIVACY_POLICY_URL)
}
R.id.linear_yhxy -> {
openAgreementPage(WebviewActivity.PAGE_AGREEMENT, AppConfig.USER_AGREEMENT_URL)
}
R.id.linear_qchc -> {
showDialogtext(
"提示",
@@ -130,14 +138,26 @@ class MineFragment : BaseFragment<MineCenterLayoutBinding, MinePresenter>(),
}
}
private fun openAgreementPage(page: Int, url: String) {
goTo(
WebviewActivity::class.java,
Bundle().apply {
putInt(WebviewActivity.EXTRA_PAGE, page)
putString(WebviewActivity.EXTRA_URL, url)
}
)
}
/** 将个人信息请求结果更新到个人中心页面。 */
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
val displayName = userInfo.userName.ifBlank { "未设置用户名" }
val displayAccount = userInfo.phoneNumber
val displayAccount = userInfo.nickName
?.ifBlank { null }
?: "未绑定手机号"
binding.tvUserName.text = displayName
binding.tvUserAccount.text = displayAccount
binding.tvChangeBindAccount.text = if (userInfo.phoneBound == true) "换绑手机号" else "换绑邮箱"
if (userInfo.avatar.isNullOrBlank()) {
binding.imgAvatar.setImageResource(R.mipmap.icon_mine_user_head_defaul)
}

View File

@@ -2,6 +2,7 @@ package com.juhabi.birds.mine
import android.os.Bundle
import android.os.CountDownTimer
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import com.gyf.immersionbar.ImmersionBar
@@ -24,6 +25,7 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
MineContract.MineView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
private var newPhoneCodeCountDownTimer: CountDownTimer? = null
private var userLoginByPhone = true
override fun createBinding(inflater: LayoutInflater): UpdateUserPhoneLayoutBinding {
return UpdateUserPhoneLayoutBinding.inflate(inflater)
}
@@ -42,9 +44,15 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.text = "修改密码"
var userinfo: MineUserInfo? = UserInfoManager.getUserInfo(mContext);
val userinfo: MineUserInfo? = UserInfoManager.getUserInfo(mContext)
userLoginByPhone = userinfo?.userLoginByPhone ?: true
binding.includedTitle.tvTitle.text = if (userinfo?.phoneBound == true) {
"更换绑定手机号"
} else {
"更换绑定邮箱"
}
binding.tvUserAccount.text = userinfo?.nickName
updateNewAccountInputMode()
listOf(
binding.tvConfirmGetcode,
binding.tvConfirmAccount,
@@ -63,8 +71,8 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
}
R.id.tv_confirm_getcode ->{
// 新接口根据登录态识别当前手机号,不需要传手机号或验证码场景。
presenter?.getCurrentPhoneCode()
if (userLoginByPhone) presenter?.getCurrentPhoneCode()
else presenter?.getCurrentEmailCode()
}
R.id.tv_confirmAccount ->{
@@ -76,25 +84,27 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
}
// 服务端校验通过后,再进入新手机号填写页面。
presenter?.verifyOldPhone(confirmCode)
if (userLoginByPhone) presenter?.verifyOldPhone(confirmCode)
else presenter?.verifyOldEmail(confirmCode)
}
R.id.tv_new_phone_getcode ->{
// 新手机号不能为空,校验通过后使用 rebind 场景发送验证码。
val newPhone = binding.etNewPhone.text.toString().trim()
if (newPhone.isBlank()) {
ToastUtil.showShort(mContext, "新手机号不能为空")
// 新账号不能为空,按手机号或邮箱模式发送 rebind 验证码。
val newAccount = binding.etNewPhone.text.toString().trim()
if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
return
}
presenter?.sendNewPhoneCode(newPhone)
if (userLoginByPhone) presenter?.sendNewPhoneCode(newAccount)
else presenter?.sendNewEmailCode(newAccount)
}
R.id.tv_submit_new_account ->{
//绑定新的手机号码
val newPhone = binding.etNewPhone.text.toString().trim()
// 根据当前换绑类型提交新手机号或新邮箱。
val newAccount = binding.etNewPhone.text.toString().trim()
val code = binding.etNewPhoneCode.text.toString().trim()
if (newPhone.isBlank()) {
ToastUtil.showShort(mContext, "新手机号不能为空")
if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
return
}
if (code.isBlank()) {
@@ -102,8 +112,8 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
return
}
// 手机号和验证码校验通过后,提交新手机号换绑请求。
presenter?.updateBindNewPhone(newPhone, code)
if (userLoginByPhone) presenter?.updateBindNewPhone(newAccount, code)
else presenter?.updateBindNewEmail(newAccount, code)
}
}
@@ -148,12 +158,24 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
override fun onVerifyOldPhoneSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
// 当前手机号验证成功后,隐藏验证区域并展示新手机号输入区域。
binding.linearConfirmUserAccount.visibility = View.GONE
binding.linearNewUserAccount.visibility = View.VISIBLE
showNewAccountInput()
}
override fun onVerifyOldEmailSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
showNewAccountInput()
}
override fun newPhoneCodeSuccess(message: String) {
startNewAccountCodeCountdown(message)
}
override fun newEmailCodeSuccess(message: String) {
startNewAccountCodeCountdown(message)
}
private fun startNewAccountCodeCountdown(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
newPhoneCodeCountDownTimer?.cancel()
@@ -191,6 +213,33 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
finish()
}
override fun onUpdateBindNewEmailSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
// 返回个人中心后,MineFragment 会在 onResume 中重新获取最新用户资料。
finish()
}
private fun showNewAccountInput() {
updateNewAccountInputMode()
binding.linearConfirmUserAccount.visibility = View.GONE
binding.linearNewUserAccount.visibility = View.VISIBLE
}
private fun updateNewAccountInputMode() {
binding.imgUserLoginType.setImageResource(
if (userLoginByPhone) R.mipmap.icon_login_phone else R.mipmap.icon_login_email
)
binding.etNewPhone.apply {
hint = if (userLoginByPhone) "请输入新手机号" else "请输入新邮箱"
inputType = if (userLoginByPhone) {
InputType.TYPE_CLASS_PHONE
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
}
}
}
override fun getUserInfoSuccess(userInfo: MineUserInfo) {
}

View File

@@ -0,0 +1,148 @@
package com.juhabi.birds.mine
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.webkit.JavascriptInterface
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActivitySerivceWebLayoutBinding
import com.juhabi.birds.mine.mvp.MinePresenter
/** Displays the user agreement or privacy policy bundled with the app. */
class WebviewActivity : BaseActivity<ActivitySerivceWebLayoutBinding, MinePresenter>() {
override fun showError(message: String) {
showMessage(message)
}
override fun initWindows() {
supportActionBar?.hide()
ImmersionBar.with(this)
.transparentStatusBar()
.statusBarColor(com.juhabi.birds.R.color.color_zhu)
.statusBarDarkFont(true)
.navigationBarColor(com.juhabi.birds.R.color.color_zhu)
.navigationBarDarkIcon(true)
.titleBar(binding.rlTitle)
.init()
}
override fun createBinding(inflater: LayoutInflater): ActivitySerivceWebLayoutBinding {
return ActivitySerivceWebLayoutBinding.inflate(inflater)
}
override fun createPresenter(): MinePresenter? = null
override fun initView(savedInstanceState: Bundle?) {
val pageCode = intent.getIntExtra(
EXTRA_PAGE,
intent.getIntExtra(LEGACY_EXTRA_PAGE, PAGE_AGREEMENT)
)
val page = when (pageCode) {
PAGE_PRIVACY -> "隐私政策" to "privacy_policy.html"
PAGE_AGREEMENT -> "用户协议" to "user_agreement.html"
else -> null
}
if (page == null) {
showMessage("页面参数无效")
finish()
return
}
binding.includeTitle.tvTitle.text = page.first
binding.includeTitle.imgBack.setOnClickListener { finish() }
val pageUrl = intent.getStringExtra(EXTRA_URL)
binding.webview.apply {
visibility = View.VISIBLE
setBackgroundColor(Color.WHITE)
settings.javaScriptEnabled = false
settings.domStorageEnabled = false
settings.allowFileAccess = false
settings.allowContentAccess = false
settings.setSupportZoom(false)
settings.builtInZoomControls = false
settings.displayZoomControls = false
settings.allowFileAccessFromFileURLs = false
settings.allowUniversalAccessFromFileURLs = false
addJavascriptInterface(PageBridge(), "Android")
webViewClient = AgreementWebViewClient()
loadUrl(pageUrl?.takeIf(String::isNotBlank) ?: "file:///android_asset/${page.second}")
}
}
override fun onBackPressed() {
if (binding.webview.canGoBack()) {
binding.webview.goBack()
} else {
super.onBackPressed()
}
}
override fun onDestroy() {
binding.webview.apply {
stopLoading()
removeJavascriptInterface("Android")
webViewClient = WebViewClient()
destroy()
}
super.onDestroy()
}
private inner class AgreementWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
return handleUrl(view, request.url)
}
@Suppress("DEPRECATION")
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
return handleUrl(view, Uri.parse(url))
}
private fun handleUrl(view: WebView, uri: Uri): Boolean {
if (uri.scheme == "file" && uri.host == "android_asset") return false
if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "mailto") {
return try {
startActivity(Intent(Intent.ACTION_VIEW, uri))
true
} catch (_: Exception) {
showMessage("无法打开此链接")
true
}
}
return true
}
override fun onReceivedError(
view: WebView,
request: WebResourceRequest,
error: WebResourceError
) {
super.onReceivedError(view, request, error)
if (request.isForMainFrame) showMessage("页面加载失败,请稍后重试")
}
}
private inner class PageBridge {
@JavascriptInterface
fun finishActivity() {
runOnUiThread { finish() }
}
}
companion object {
const val EXTRA_PAGE = "from_request_code"
const val EXTRA_URL = "webview_url"
const val LEGACY_EXTRA_PAGE = "from_requse_code"
const val PAGE_AGREEMENT = 10001
const val PAGE_PRIVACY = 10002
}
}

View File

@@ -11,5 +11,6 @@ data class MineUserInfo(
val phoneBound: Boolean = false,
val userType: String = "",
val createTime: String = "",
val avatar: String? = null
val avatar: String? = null,
val userLoginByPhone : Boolean = false
)

View File

@@ -21,13 +21,17 @@ interface MineContract {
)
fun modifyPassword(oldPassword: String, newPassword: String, callback: Callback<String>)
fun getCurrentPhoneCode(callback: Callback<BaseBean<Any>>)
fun getCurrentEmailCode(callback: Callback<BaseBean<Any>>)
fun sendPhoneCode(request: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
fun sendEmailCode(request: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
fun verifyOldPhone(oldCode: String, callback: Callback<BaseBean<Any>>)
fun verifyOldEmail(oldCode: String, callback: Callback<BaseBean<Any>>)
fun updateBindNewPhone(
newPhoneNumber: String,
code: String,
callback: Callback<BaseBean<Any>>
)
fun updateBindNewEmail(newEmail: String, code: String, callback: Callback<BaseBean<Any>>)
fun userLogout(callback: Callback<BaseBean<Any>>)
fun userLogoff(callback: Callback<BaseBean<Any>>)
fun cancel()
@@ -46,6 +50,9 @@ interface MineContract {
fun onVerifyOldPhoneSuccess(message: String) {}
/** 新手机号绑定成功回调。 */
fun onUpdateBindNewPhoneSuccess(message: String) {}
fun onUpdateBindNewEmailSuccess(message: String) {}
fun onVerifyOldEmailSuccess(message: String) {}
fun newEmailCodeSuccess(message: String) {}
/** 退出登录成功回调。 */
fun onLogoutSuccess(message: String) {}
/** 注销账号成功回调。 */

View File

@@ -55,6 +55,17 @@ class MineImpl : MineContract.DataModel {
}
}
override fun getCurrentEmailCode(callback: MineContract.Callback<BaseBean<Any>>) {
ApiClient.apiService.getCurrentEmailCode(NetworkManager.getToken()).enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("验证码发送失败") }
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
}
})
}
override fun sendPhoneCode(
request: JSONObject,
callback: MineContract.Callback<BaseBean<SendCodeEntity>>
@@ -86,6 +97,17 @@ class MineImpl : MineContract.DataModel {
}
}
override fun sendEmailCode(request: JSONObject, callback: MineContract.Callback<BaseBean<SendCodeEntity>>) {
ApiClient.apiService.sendEmailCode(NetworkManager.getToken(), NetworkManager.toRequestBody(request)).enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(call: Call<BaseBean<SendCodeEntity>>, response: Response<BaseBean<SendCodeEntity>>) {
mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("验证码发送失败") }
}
override fun onFailure(call: Call<BaseBean<SendCodeEntity>>, error: Throwable) {
if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
}
})
}
override fun verifyOldPhone(
oldCode: String,
callback: MineContract.Callback<BaseBean<Any>>
@@ -119,6 +141,18 @@ class MineImpl : MineContract.DataModel {
}
}
override fun verifyOldEmail(oldCode: String, callback: MineContract.Callback<BaseBean<Any>>) {
val body = JSONObject().apply { put("oldCode", oldCode.trim()) }
ApiClient.apiService.verifyOldEmail(NetworkManager.getToken(), NetworkManager.toRequestBody(body)).enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("当前邮箱验证失败") }
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
}
})
}
override fun updateBindNewPhone(
newPhoneNumber: String,
code: String,
@@ -157,6 +191,18 @@ class MineImpl : MineContract.DataModel {
}
}
override fun updateBindNewEmail(newEmail: String, code: String, callback: MineContract.Callback<BaseBean<Any>>) {
val body = JSONObject().apply { put("newEmail", newEmail.trim()); put("code", code.trim()) }
ApiClient.apiService.updateBindNewEmail(NetworkManager.getToken(), NetworkManager.toRequestBody(body)).enqueue(object : Callback<BaseBean<Any>> {
override fun onResponse(call: Call<BaseBean<Any>>, response: Response<BaseBean<Any>>) {
mainHandler.post { response.body()?.let(callback::success) ?: callback.fail("邮箱更换失败") }
}
override fun onFailure(call: Call<BaseBean<Any>>, error: Throwable) {
if (!call.isCanceled) mainHandler.post { callback.fail(error.message ?: "网络请求失败") }
}
})
}
override fun userLogout(callback: MineContract.Callback<BaseBean<Any>>) {
logoutCall?.cancel()
logoutCall = ApiClient.apiService

View File

@@ -3,8 +3,8 @@ 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 com.juhabi.birds.network.BaseBean
import org.json.JSONObject
class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract.MineView>() {
@@ -34,6 +34,31 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
})
}
/** 通过登录态向当前绑定的邮箱发送验证码。 */
fun getCurrentEmailCode() {
val currentView = view ?: return
currentView.showLoading()
mineImpl.getCurrentEmailCode(object : MineContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
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
@@ -63,6 +88,35 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
})
}
/** 向新邮箱发送 rebind 场景验证码。 */
fun sendNewEmailCode(email: String) {
val currentView = view ?: return
currentView.showLoading()
val request = JSONObject().apply {
put("email", email.trim())
put("scene", com.juhabi.birds.AppConfig.SEND_CODE_SCENE_REBIND)
}
mineImpl.sendEmailCode(request, object : MineContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
newEmailCodeSuccess(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
@@ -90,6 +144,33 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
})
}
/** 校验当前邮箱收到的验证码。 */
fun verifyOldEmail(oldCode: String) {
val currentView = view ?: return
currentView.showLoading()
mineImpl.verifyOldEmail(oldCode, object : MineContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
onVerifyOldEmailSuccess(
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
@@ -121,6 +202,33 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
)
}
/** 提交新邮箱和验证码,完成邮箱换绑。 */
fun updateBindNewEmail(email: String, code: String) {
val currentView = view ?: return
currentView.showLoading()
mineImpl.updateBindNewEmail(email, code, object : MineContract.Callback<BaseBean<Any>> {
override fun success(data: BaseBean<Any>) {
view?.apply {
hideLoading()
if (data.code == 0 || data.code == 200) {
onUpdateBindNewEmailSuccess(
data.message.ifBlank { "邮箱更换成功" }
)
} else {
showError(data.message.ifBlank { "邮箱更换失败" })
}
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
}
/** 调用服务端退出登录接口,并统一分发成功或失败结果。 */
fun userLogout() {
val currentView = view ?: return
@@ -188,8 +296,7 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
showError(message)
}
}
}
)
})
}
fun checkVersion(currentVersion: String) {

View File

@@ -235,6 +235,44 @@ interface ApiService {
fun updateBindNewPhone(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<Any>>
/*****************************************************************************************************************************************/
/**
* 验证原邮箱号--发送短信
*
* 不需要任何入参,
*/
@POST("app/user/email/old-code")
fun getCurrentEmailCode(@HeaderMap headers: Map<String, String>): Call<BaseBean<Any>>
/**
* 验证原邮箱号--验证邮箱号
*
* 入参
*{
* "oldCode": "123456"
* }
*
*/
@POST("app/user/email/verify-old")
fun verifyOldEmail(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<Any>>
/**
* 更换新的绑定邮箱号
{
"newEmail": "string",
"code": "string"
}
*/
@PUT("app/user/email")
fun updateBindNewEmail(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<Any>>
/*****************************************************************************************************************************************/
/**
* 退出登录
*

View File

@@ -40,7 +40,13 @@ object UserInfoManager {
@JvmStatic
fun setUserInfo(userInfo: MineUserInfo) {
setUserInfo(MyApplication.appContext, userInfo)
val loginByPhone = userInfo.email.isNullOrBlank()
val normalizedUserInfo = userInfo.copy(
email = if (loginByPhone) "" else userInfo.email,
phoneNumber = if (loginByPhone) userInfo.phoneNumber else "",
userLoginByPhone = loginByPhone
)
setUserInfo(MyApplication.appContext, normalizedUserInfo)
}
@JvmStatic

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/color_zhu">
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_zhu">
<include
android:id="@+id/include_title"
layout="@layout/include_activity_back_gray_layout"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"/>
<ImageView
android:visibility="gone"
android:padding="20dp"
android:onClick="blck"
android:id="@+id/mIvtitle_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="OnClick" />
</RelativeLayout>
</LinearLayout>

View File

@@ -43,11 +43,13 @@
android:paddingStart="14dp">
<ImageView
android:id="@+id/img_login_type"
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_phone"
android:contentDescription="@null" />
<LinearLayout
android:id="@+id/linear_country"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"

View File

@@ -107,6 +107,7 @@
android:src="@mipmap/icon_mine_hbsj"/>
<TextView
android:id="@+id/tv_change_bind_account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="9"
@@ -114,7 +115,7 @@
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/white"
android:text="换绑手机号"/>
android:text="换绑"/>
<ImageView
android:layout_width="0dp"

View File

@@ -43,6 +43,7 @@
android:paddingStart="14dp">
<ImageView
android:id="@+id/img_login_type"
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_phone"

View File

@@ -47,7 +47,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="13sp"
android:textSize="16sp"
android:textColor="@color/color_black1"
android:text=""/>
</LinearLayout>
@@ -135,6 +135,7 @@
android:paddingStart="14dp">
<ImageView
android:id="@+id/img_user_login_type"
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_phone"