隐私政策,用户协议

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:name=".mine.UpdateUserAccount"
android:exported="false" /> android:exported="false" />
<activity
android:name=".mine.WebviewActivity"
android:exported="false" />
<provider <provider

View File

@@ -3,6 +3,8 @@ package com.juhabi.birds
object AppConfig { object AppConfig {
// const val BASE_URL = "http://47.97.217.123:8081" // const val BASE_URL = "http://47.97.217.123:8081"
const val BASE_URL = "http://192.168.0.212: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_CLIENT_ID = "app"
const val APP_USER_TYPE = "app_user" const val APP_USER_TYPE = "app_user"
const val CONTENT_LANGUAGE = "zh_CN" const val CONTENT_LANGUAGE = "zh_CN"

View File

@@ -24,6 +24,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
LoginContract.LoginView, View.OnClickListener { LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null private var codeCountDownTimer: CountDownTimer? = null
private var resetByPhone = true
private var isPasswordVisible = false private var isPasswordVisible = false
private var isConfirmPasswordVisible = false private var isConfirmPasswordVisible = false
@@ -52,6 +53,7 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
binding.passwordVisibilityButton, binding.passwordVisibilityButton,
binding.confirmPasswordVisibilityButton, binding.confirmPasswordVisibilityButton,
binding.confirmButton, binding.confirmButton,
binding.tvChangeLoginType,
).forEach { view -> ).forEach { view ->
view.setOnClickListener(this) view.setOnClickListener(this)
} }
@@ -63,13 +65,35 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
finish() 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 -> {//获取验证码 R.id.getCodeButton -> {//获取验证码
val phone = binding.phoneEditText.text.toString().trim() val account = binding.phoneEditText.text.toString().trim()
if (phone.isBlank()) { if (account.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空") ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
return 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 -> { R.id.passwordVisibilityButton -> {
@@ -83,13 +107,13 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
} }
R.id.confirmButton -> {//修改密码 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 code = binding.codeEditText.text.toString().trim()
val password = binding.passwordEditText.text.toString() val password = binding.passwordEditText.text.toString()
val confirmPassword = binding.confirmPasswordEditText.text.toString() val confirmPassword = binding.confirmPasswordEditText.text.toString()
if (phone.isBlank()) { if (account.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空") ToastUtil.showShort(mContext, if (resetByPhone) "手机号不能为空" else "邮箱不能为空")
return return
} }
if (code.isBlank()) { if (code.isBlank()) {
@@ -116,7 +140,11 @@ class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>()
return 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 ->{ R.id.tv_change_login_type ->{
registerByPhone = !registerByPhone registerByPhone = !registerByPhone
binding.linearCountry.visibility = if (registerByPhone) View.VISIBLE else View.GONE 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.tvChangeLoginType.text = if (registerByPhone) "邮箱" else "手机"
binding.phoneEditText.apply { binding.phoneEditText.apply {
text?.clear() text?.clear()

View File

@@ -68,6 +68,14 @@ class MineFragment : BaseFragment<MineCenterLayoutBinding, MinePresenter>(),
goTo(UpdateUserAccount::class.java, null) 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 -> { R.id.linear_qchc -> {
showDialogtext( 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) { override fun getUserInfoSuccess(userInfo: MineUserInfo) {
val displayName = userInfo.userName.ifBlank { "未设置用户名" } val displayName = userInfo.userName.ifBlank { "未设置用户名" }
val displayAccount = userInfo.phoneNumber val displayAccount = userInfo.nickName
?.ifBlank { null } ?.ifBlank { null }
?: "未绑定手机号" ?: "未绑定手机号"
binding.tvUserName.text = displayName binding.tvUserName.text = displayName
binding.tvUserAccount.text = displayAccount binding.tvUserAccount.text = displayAccount
binding.tvChangeBindAccount.text = if (userInfo.phoneBound == true) "换绑手机号" else "换绑邮箱"
if (userInfo.avatar.isNullOrBlank()) { if (userInfo.avatar.isNullOrBlank()) {
binding.imgAvatar.setImageResource(R.mipmap.icon_mine_user_head_defaul) 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.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.text.InputType
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import com.gyf.immersionbar.ImmersionBar import com.gyf.immersionbar.ImmersionBar
@@ -24,6 +25,7 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
MineContract.MineView, View.OnClickListener { MineContract.MineView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null private var codeCountDownTimer: CountDownTimer? = null
private var newPhoneCodeCountDownTimer: CountDownTimer? = null private var newPhoneCodeCountDownTimer: CountDownTimer? = null
private var userLoginByPhone = true
override fun createBinding(inflater: LayoutInflater): UpdateUserPhoneLayoutBinding { override fun createBinding(inflater: LayoutInflater): UpdateUserPhoneLayoutBinding {
return UpdateUserPhoneLayoutBinding.inflate(inflater) return UpdateUserPhoneLayoutBinding.inflate(inflater)
} }
@@ -42,9 +44,15 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
override fun initView(savedInstanceState: Bundle?) { override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.text = "修改密码" val userinfo: MineUserInfo? = UserInfoManager.getUserInfo(mContext)
var userinfo: MineUserInfo? = UserInfoManager.getUserInfo(mContext); userLoginByPhone = userinfo?.userLoginByPhone ?: true
binding.includedTitle.tvTitle.text = if (userinfo?.phoneBound == true) {
"更换绑定手机号"
} else {
"更换绑定邮箱"
}
binding.tvUserAccount.text = userinfo?.nickName binding.tvUserAccount.text = userinfo?.nickName
updateNewAccountInputMode()
listOf( listOf(
binding.tvConfirmGetcode, binding.tvConfirmGetcode,
binding.tvConfirmAccount, binding.tvConfirmAccount,
@@ -63,8 +71,8 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
} }
R.id.tv_confirm_getcode ->{ R.id.tv_confirm_getcode ->{
// 新接口根据登录态识别当前手机号,不需要传手机号或验证码场景。 if (userLoginByPhone) presenter?.getCurrentPhoneCode()
presenter?.getCurrentPhoneCode() else presenter?.getCurrentEmailCode()
} }
R.id.tv_confirmAccount ->{ 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 ->{ R.id.tv_new_phone_getcode ->{
// 新手机号不能为空,校验通过后使用 rebind 场景发送验证码。 // 新账号不能为空,按手机号或邮箱模式发送 rebind 验证码。
val newPhone = binding.etNewPhone.text.toString().trim() val newAccount = binding.etNewPhone.text.toString().trim()
if (newPhone.isBlank()) { if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, "新手机号不能为空") ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
return return
} }
presenter?.sendNewPhoneCode(newPhone) if (userLoginByPhone) presenter?.sendNewPhoneCode(newAccount)
else presenter?.sendNewEmailCode(newAccount)
} }
R.id.tv_submit_new_account ->{ 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() val code = binding.etNewPhoneCode.text.toString().trim()
if (newPhone.isBlank()) { if (newAccount.isBlank()) {
ToastUtil.showShort(mContext, "新手机号不能为空") ToastUtil.showShort(mContext, if (userLoginByPhone) "新手机号不能为空" else "新邮箱不能为空")
return return
} }
if (code.isBlank()) { if (code.isBlank()) {
@@ -102,8 +112,8 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
return return
} }
// 手机号和验证码校验通过后,提交新手机号换绑请求。 if (userLoginByPhone) presenter?.updateBindNewPhone(newAccount, code)
presenter?.updateBindNewPhone(newPhone, code) else presenter?.updateBindNewEmail(newAccount, code)
} }
} }
@@ -148,12 +158,24 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
override fun onVerifyOldPhoneSuccess(message: String) { override fun onVerifyOldPhoneSuccess(message: String) {
hideLoading() hideLoading()
ToastUtil.showShort(mContext, message) ToastUtil.showShort(mContext, message)
// 当前手机号验证成功后,隐藏验证区域并展示新手机号输入区域。 showNewAccountInput()
binding.linearConfirmUserAccount.visibility = View.GONE }
binding.linearNewUserAccount.visibility = View.VISIBLE
override fun onVerifyOldEmailSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
showNewAccountInput()
} }
override fun newPhoneCodeSuccess(message: String) { override fun newPhoneCodeSuccess(message: String) {
startNewAccountCodeCountdown(message)
}
override fun newEmailCodeSuccess(message: String) {
startNewAccountCodeCountdown(message)
}
private fun startNewAccountCodeCountdown(message: String) {
hideLoading() hideLoading()
ToastUtil.showShort(mContext, message) ToastUtil.showShort(mContext, message)
newPhoneCodeCountDownTimer?.cancel() newPhoneCodeCountDownTimer?.cancel()
@@ -191,6 +213,33 @@ class UpdateUserAccount : BaseActivity<UpdateUserPhoneLayoutBinding, MinePresen
finish() 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) { 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 phoneBound: Boolean = false,
val userType: String = "", val userType: String = "",
val createTime: 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 modifyPassword(oldPassword: String, newPassword: String, callback: Callback<String>)
fun getCurrentPhoneCode(callback: Callback<BaseBean<Any>>) fun getCurrentPhoneCode(callback: Callback<BaseBean<Any>>)
fun getCurrentEmailCode(callback: Callback<BaseBean<Any>>)
fun sendPhoneCode(request: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>) 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 verifyOldPhone(oldCode: String, callback: Callback<BaseBean<Any>>)
fun verifyOldEmail(oldCode: String, callback: Callback<BaseBean<Any>>)
fun updateBindNewPhone( fun updateBindNewPhone(
newPhoneNumber: String, newPhoneNumber: String,
code: String, code: String,
callback: Callback<BaseBean<Any>> callback: Callback<BaseBean<Any>>
) )
fun updateBindNewEmail(newEmail: String, code: String, callback: Callback<BaseBean<Any>>)
fun userLogout(callback: Callback<BaseBean<Any>>) fun userLogout(callback: Callback<BaseBean<Any>>)
fun userLogoff(callback: Callback<BaseBean<Any>>) fun userLogoff(callback: Callback<BaseBean<Any>>)
fun cancel() fun cancel()
@@ -46,6 +50,9 @@ interface MineContract {
fun onVerifyOldPhoneSuccess(message: String) {} fun onVerifyOldPhoneSuccess(message: String) {}
/** 新手机号绑定成功回调。 */ /** 新手机号绑定成功回调。 */
fun onUpdateBindNewPhoneSuccess(message: String) {} fun onUpdateBindNewPhoneSuccess(message: String) {}
fun onUpdateBindNewEmailSuccess(message: String) {}
fun onVerifyOldEmailSuccess(message: String) {}
fun newEmailCodeSuccess(message: String) {}
/** 退出登录成功回调。 */ /** 退出登录成功回调。 */
fun onLogoutSuccess(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( override fun sendPhoneCode(
request: JSONObject, request: JSONObject,
callback: MineContract.Callback<BaseBean<SendCodeEntity>> 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( override fun verifyOldPhone(
oldCode: String, oldCode: String,
callback: MineContract.Callback<BaseBean<Any>> 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( override fun updateBindNewPhone(
newPhoneNumber: String, newPhoneNumber: String,
code: 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>>) { override fun userLogout(callback: MineContract.Callback<BaseBean<Any>>) {
logoutCall?.cancel() logoutCall?.cancel()
logoutCall = ApiClient.apiService 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.base.BasePresenter
import com.juhabi.birds.main.model.AppVersionInfo import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.mine.model.MineUserInfo import com.juhabi.birds.mine.model.MineUserInfo
import com.juhabi.birds.network.BaseBean
import com.juhabi.birds.login.model.SendCodeEntity import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject import org.json.JSONObject
class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract.MineView>() { 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 场景验证码。 */ /** 向新手机号发送 rebind 场景验证码。 */
fun sendNewPhoneCode(phoneNumber: String) { fun sendNewPhoneCode(phoneNumber: String) {
val currentView = view ?: return 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) { fun verifyOldPhone(oldCode: String) {
val currentView = view ?: return 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) { fun updateBindNewPhone(newPhoneNumber: String, code: String) {
val currentView = view ?: return 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() { fun userLogout() {
val currentView = view ?: return val currentView = view ?: return
@@ -175,21 +283,20 @@ class MinePresenter(private val mineImpl: MineImpl) : BasePresenter<MineContract
val currentView = view ?: return val currentView = view ?: return
currentView.showLoading() currentView.showLoading()
mineImpl.getUserInfo(emptyMap(), object : MineContract.Callback<MineUserInfo> { mineImpl.getUserInfo(emptyMap(), object : MineContract.Callback<MineUserInfo> {
override fun success(data: MineUserInfo) { override fun success(data: MineUserInfo) {
view?.apply { view?.apply {
hideLoading() hideLoading()
getUserInfoSuccess(data) getUserInfoSuccess(data)
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
} }
} }
)
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
})
} }
fun checkVersion(currentVersion: String) { 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>> 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 @JvmStatic
fun setUserInfo(userInfo: MineUserInfo) { 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 @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"> android:paddingStart="14dp">
<ImageView <ImageView
android:id="@+id/img_login_type"
android:layout_width="28dp" android:layout_width="28dp"
android:layout_height="28dp" android:layout_height="28dp"
android:src="@mipmap/icon_login_phone" android:src="@mipmap/icon_login_phone"
android:contentDescription="@null" /> android:contentDescription="@null" />
<LinearLayout <LinearLayout
android:id="@+id/linear_country"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="horizontal" android:orientation="horizontal"

View File

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

View File

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

View File

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