登录相关,首次提交

This commit is contained in:
fengjignqi
2026-09-18 09:59:43 +08:00
parent 6e9b97e42e
commit f7cf112dc9
45 changed files with 2362 additions and 177 deletions

View File

@@ -41,7 +41,7 @@
android:exported="false" />
<activity
android:name=".main.MainActivity"
android:name=".login.WelcomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -49,10 +49,24 @@
</intent-filter>
</activity>
<activity
android:name=".main.MainActivity"
android:exported="false" />
<activity
android:name=".login.LoginActivity"
android:exported="false" />
<activity
android:name=".login.RegisterActivity"
android:exported="false" />
<activity
android:name=".login.ForgetPsdActivity"
android:exported="false" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"

View File

@@ -1,11 +1,21 @@
package com.juhabi.birds
object AppConfig {
const val BASE_URL = "http://47.97.217.123:8081/"
const val APP_CLIENT_ID = "428a8310cd442757ae699df5d894f051"
// const val BASE_URL = "http://47.97.217.123:8081"
const val BASE_URL = "http://192.168.1.217:8081/"
const val APP_CLIENT_ID = "app"
const val APP_USER_TYPE = "app_user"
const val CONTENT_LANGUAGE = "zh_CN"
//验证码场景 --注册
const val SEND_CODE_SCENE_REGISTER = "register"
//验证码场景 --登录
const val SEND_CODE_SCENE_LOGIN = "login"
//验证码场景 --重置密码
const val SEND_CODE_SCENE_RESET = "resetPwd"
@Volatile
var userToken: String = ""
//是否登录
var USER_LOGIN: Boolean = false;
}

View File

@@ -6,6 +6,8 @@ import android.app.Application
import android.content.Context
import android.os.Build
import android.os.Bundle
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil
import com.juhabi.birds.util.manager.UserInfoManager
import me.jessyan.autosize.AutoSize
import me.jessyan.autosize.AutoSizeConfig
@@ -36,9 +38,21 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
/** Add application-wide, privacy-safe SDK initialization here. */
private fun initialize() {
UserInfoManager.initialize(this)
restoreLoginSession()
// NetworkManager is created lazily by ApiClient, so it needs no eager initialization.
}
/**
* 启动时从本地恢复 token,写入 AppConfig.userToken,
* 业务接口通过 NetworkManager.getToken() 自动带上 Authorization。
*/
private fun restoreLoginSession() {
val token = TokenManager.get(this).trim()
AppConfig.userToken = token
AppConfig.USER_LOGIN = token.isNotBlank() &&
SharedPreferencesUtil.getBoolean(this, KEY_USER_LOGIN, defaultValue = token.isNotBlank())
}
override fun onActivityResumed(activity: Activity) {
currentActivity = activity
}
@@ -80,6 +94,8 @@ class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
}
companion object {
private const val KEY_USER_LOGIN = "user_login"
lateinit var instance: MyApplication
private set

View File

@@ -0,0 +1,217 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.os.CountDownTimer
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ForgetPsdLayoutBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.util.ToastUtil
/**
* 忘记密码 / 修改密码
*/
class ForgetPsdActivity : BaseActivity<ForgetPsdLayoutBinding, LoginPresenter>(),
LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
private var isPasswordVisible = false
private var isConfirmPasswordVisible = false
override fun createBinding(inflater: LayoutInflater): ForgetPsdLayoutBinding {
return ForgetPsdLayoutBinding.inflate(inflater)
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.statusBarView(binding.statusBarView)
.statusBarDarkFont(true)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.setText(R.string.forget_password_title)
listOf(
binding.includedTitle.imgBack,
binding.getCodeButton,
binding.passwordVisibilityButton,
binding.confirmPasswordVisibilityButton,
binding.confirmButton,
).forEach { view ->
view.setOnClickListener(this)
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.img_back -> {
finish()
}
R.id.getCodeButton -> {//获取验证码
val phone = binding.phoneEditText.text.toString().trim()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(phone, AppConfig.SEND_CODE_SCENE_RESET)
}
R.id.passwordVisibilityButton -> {
isPasswordVisible = !isPasswordVisible
togglePasswordVisibility(binding.passwordEditText, isPasswordVisible)
}
R.id.confirmPasswordVisibilityButton -> {
isConfirmPasswordVisible = !isConfirmPasswordVisible
togglePasswordVisibility(binding.confirmPasswordEditText, isConfirmPasswordVisible)
}
R.id.confirmButton -> {//修改密码
val phone = 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, "手机号不能为空")
return
}
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
if (confirmPassword.isBlank()) {
ToastUtil.showShort(mContext, "请再次输入密码")
return
}
if (password != confirmPassword) {
ToastUtil.showShort(mContext, "两次输入的密码不一致")
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
"密码需为8-20位,且包含大小写字母和数字"
)
return
}
presenter?.forgetPsdByPhone(phone, code, password)
}
}
}
private fun togglePasswordVisibility(editText: EditText, visible: Boolean) {
val selection = editText.selectionEnd
editText.inputType = if (visible) {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
editText.setSelection(selection.coerceAtLeast(0))
editText.typeface = binding.phoneEditText.typeface
}
override fun showError(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.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.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message.ifBlank { "密码修改成功" })
finish()
}
override fun showUsernameError(message: String) {
ToastUtil.showShort(mContext, message)
}
override fun showPasswordError(message: String) {
ToastUtil.showShort(mContext, message)
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
/** 8-20 位,且同时包含大写、小写字母和数字。 */
private fun isValidPassword(password: String): Boolean {
if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) {
return false
}
val hasUpper = password.any { it.isUpperCase() }
val hasLower = password.any { it.isLowerCase() }
val hasDigit = password.any { it.isDigit() }
return hasUpper && hasLower && hasDigit
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val PASSWORD_MIN_LENGTH = 8
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -1,20 +1,35 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.content.Intent
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
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActivityLoginBinding
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.mine.MineCenterActivity
import com.juhabi.birds.network.TokenManager
import com.juhabi.birds.util.SharedPreferencesUtil
import com.juhabi.birds.util.ToastUtil
/**
* 登录
*/
class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
LoginContract.LoginView {
LoginContract.LoginView, View.OnClickListener {
/** 是否使用密码登录;false 为手机号 + 验证码登录 */
private var loginByPassword = true
private var isPasswordVisible = false
private var codeCountDownTimer: CountDownTimer? = null
override fun createBinding(inflater: LayoutInflater): ActivityLoginBinding {
return ActivityLoginBinding.inflate(inflater)
@@ -24,48 +39,176 @@ class LoginActivity : BaseActivity<ActivityLoginBinding, LoginPresenter>(),
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
// if (TokenManager.get(this).isNotBlank()) {
// startActivity(Intent(this, MineCenterActivity::class.java))
// finish()
// return
// }
if(binding.usernameEditText.text.toString().isNotBlank()){
ToastUtil.showShort(mContext,"");
return
listOf(
binding.tvChangeCode,
binding.forgotPasswordButton,
binding.passwordVisibilityButton,
binding.getCodeButton,
binding.loginButton,
binding.registerButton,
).forEach { button ->
button.setOnClickListener(this)
}
binding.loginButton.setOnClickListener {
presenter?.login(
binding.usernameEditText.text?.toString().orEmpty(),
binding.passwordEditText.text?.toString().orEmpty()
)
}
override fun onClick(view: View) {
when (view.id) {
R.id.tvChangeCode -> {//更换登录方式
if (loginByPassword) {
binding.linearCode.visibility = View.VISIBLE
binding.linearPsd.visibility = View.GONE
loginByPassword = false
binding.tvChangeCode.setText(R.string.register_change_psd_login)
} else {
binding.linearCode.visibility = View.GONE
binding.linearPsd.visibility = View.VISIBLE
loginByPassword = true
binding.tvChangeCode.setText(R.string.register_change_code_login)
}
}
R.id.getCodeButton -> {//获取验证码
val phone = binding.accountEditText.text.toString().trim()
if (phone.isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(phone, AppConfig.SEND_CODE_SCENE_LOGIN)
}
R.id.registerButton -> {//注册
goTo(RegisterActivity::class.java, null)
}
R.id.forgotPasswordButton -> {//忘记密码
goTo(ForgetPsdActivity::class.java, null)
}
R.id.passwordVisibilityButton -> {
togglePasswordVisibility()
}
R.id.loginButton -> {//登录
val account = binding.accountEditText.text.toString().trim()
if (account.isBlank()) {
ToastUtil.showShort(mContext, "账号不能为空")
return
}
if (loginByPassword) {
val password = binding.passwordEditText.text.toString()
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
presenter?.loginByPhonePsd(account, password)
} else {
val code = binding.codeEditText.text.toString().trim()
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
presenter?.loginByPhoneSms(account, code)
}
}
}
}
private fun togglePasswordVisibility() {
isPasswordVisible = !isPasswordVisible
val selection = binding.passwordEditText.selectionEnd
binding.passwordEditText.inputType = if (isPasswordVisible) {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
} else {
InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
// 保持光标位置,并触发 typeface 刷新
binding.passwordEditText.setSelection(selection.coerceAtLeast(0))
binding.passwordEditText.typeface = binding.accountEditText.typeface
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.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.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
binding.usernameLayout.error = message
binding.usernameEditText.requestFocus()
ToastUtil.showShort(mContext, message)
}
override fun showPasswordError(message: String) {
binding.passwordLayout.error = message
binding.passwordEditText.requestFocus()
ToastUtil.showShort(mContext, message)
}
override fun clearInputErrors() {
binding.usernameLayout.error = null
binding.passwordLayout.error = null
}
override fun onLoginSuccess(user: LoginUser) {
TokenManager.save(this, user.accessToken)
binding.resultTextView.text = "登录成功,Token 已保存"
showMessage("登录成功")
override fun onLoginSuccess(user: UserEntity) {
val token = user.token.trim()
// SharedPreferences 持久化 token,并同步到内存 AppConfig.userToken
TokenManager.save(this, token)
AppConfig.USER_LOGIN = token.isNotBlank()
SharedPreferencesUtil.putBoolean(this, KEY_USER_LOGIN, AppConfig.USER_LOGIN)
ToastUtil.showShort(mContext, "登录成功")
startActivity(Intent(this, MineCenterActivity::class.java))
finish()
}
override fun showError(message: String) {
ToastUtil.showShort(mContext,message)
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val KEY_USER_LOGIN = "user_login"
}
}

View File

@@ -0,0 +1,192 @@
package com.juhabi.birds.login
import android.os.Bundle
import android.os.CountDownTimer
import android.view.LayoutInflater
import android.view.View
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.RegisterLayoutBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginImpl
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.util.ToastUtil
/**
* 注册
*/
class RegisterActivity : BaseActivity<RegisterLayoutBinding, LoginPresenter>(),
LoginContract.LoginView, View.OnClickListener {
private var codeCountDownTimer: CountDownTimer? = null
override fun createBinding(inflater: LayoutInflater): RegisterLayoutBinding {
return RegisterLayoutBinding.inflate(inflater)
}
override fun createPresenter(): LoginPresenter? {
return LoginPresenter(LoginImpl())
}
override fun initWindows() {
ImmersionBar.with(this)
.statusBarView(binding.statusBarView)
.statusBarDarkFont(true)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun initView(savedInstanceState: Bundle?) {
binding.includedTitle.tvTitle.setText(R.string.login_register)
listOf(
binding.passwordVisibilityButton,
binding.getCodeButton,
binding.confirmButton,
binding.includedTitle.imgBack,
).forEach { button ->
button.setOnClickListener(this)
}
}
override fun onClick(view: View) {
when (view.id) {
R.id.img_back ->{
finish()
}
R.id.getCodeButton -> {//获取验证码
if (binding.phoneEditText.text.toString().isBlank()) {
ToastUtil.showShort(mContext, "手机号不能为空")
return
}
presenter?.getPhoneSmsCode(
binding.phoneEditText.text.toString(),
AppConfig.SEND_CODE_SCENE_REGISTER
)
}
R.id.confirmButton -> {//注册
val phone = 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, "手机号不能为空")
return
}
if (code.isBlank()) {
ToastUtil.showShort(mContext, "验证码不能为空")
return
}
if (password.isBlank()) {
ToastUtil.showShort(mContext, "密码不能为空")
return
}
if (confirmPassword.isBlank()) {
ToastUtil.showShort(mContext, "请再次输入密码")
return
}
if (password != confirmPassword) {
ToastUtil.showShort(mContext, "两次输入的密码不一致")
return
}
if (!isValidPassword(password)) {
ToastUtil.showShort(
mContext,
"密码需为8-20位,且包含大小写字母和数字"
)
return
}
presenter?.registerByPhone(phone, code, password)
}
}
}
override fun showError(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
}
override fun sendCodeSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message)
codeCountDownTimer?.cancel()
binding.getCodeButton.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.getCodeButton.text = getString(
R.string.register_code_countdown,
secondsRemaining
)
}
override fun onFinish() {
codeCountDownTimer = null
binding.getCodeButton.apply {
isEnabled = true
isClickable = true
setBackgroundResource(R.drawable.shape_register_code_bg)
setText(R.string.register_get_code)
}
}
}.start()
}
override fun onRegisterSuccess(message: String) {
hideLoading()
ToastUtil.showShort(mContext, message.ifBlank { "注册成功" })
finish()
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
}
override fun showPasswordError(message: String) {
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
codeCountDownTimer?.cancel()
codeCountDownTimer = null
super.onDestroy()
}
/** 8-20 位,且同时包含大写、小写字母和数字。 */
private fun isValidPassword(password: String): Boolean {
if (password.length !in PASSWORD_MIN_LENGTH..PASSWORD_MAX_LENGTH) {
return false
}
val hasUpper = password.any { it.isUpperCase() }
val hasLower = password.any { it.isLowerCase() }
val hasDigit = password.any { it.isDigit() }
return hasUpper && hasLower && hasDigit
}
private companion object {
const val CODE_COUNTDOWN_SECONDS = 60L
const val CODE_COUNTDOWN_MILLIS = CODE_COUNTDOWN_SECONDS * 1_000L
const val PASSWORD_MIN_LENGTH = 8
const val PASSWORD_MAX_LENGTH = 20
}
}

View File

@@ -0,0 +1,86 @@
package com.juhabi.birds.login
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.LayoutInflater
import com.gyf.immersionbar.ImmersionBar
import com.juhabi.birds.AppConfig
import com.juhabi.birds.R
import com.juhabi.birds.base.BaseActivity
import com.juhabi.birds.databinding.ActiveWelcomeBinding
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.mvp.LoginContract
import com.juhabi.birds.login.mvp.LoginPresenter
import com.juhabi.birds.main.MainActivity
/**
* 启动页面
*/
class WelcomeActivity: BaseActivity<ActiveWelcomeBinding,LoginPresenter>(),LoginContract.LoginView {
private val handler = Handler(Looper.getMainLooper())
private val navigateRunnable = Runnable {
val targetActivity = if (AppConfig.USER_LOGIN) {
MainActivity::class.java
} else {
LoginActivity::class.java
}
startActivity(Intent(this, targetActivity))
finish()
}
override fun createBinding(inflater: LayoutInflater): ActiveWelcomeBinding {
return ActiveWelcomeBinding.inflate(inflater)
}
override fun initWindows() {
ImmersionBar.with(this)
.navigationBarColor(R.color.color_zhu)
.navigationBarDarkIcon(true)
.init()
}
override fun createPresenter(): LoginPresenter? {
return null
}
override fun initView(savedInstanceState: Bundle?) {
handler.postDelayed(navigateRunnable, SPLASH_DELAY_MILLIS)
}
override fun showError(message: String) {
}
override fun sendCodeSuccess(message: String) {
}
override fun onRegisterSuccess(message: String) {
}
override fun onForgetPsdSuccess(message: String) {
}
override fun showUsernameError(message: String) {
}
override fun showPasswordError(message: String) {
}
override fun clearInputErrors() {
}
override fun onLoginSuccess(user: UserEntity) {
}
override fun onDestroy() {
handler.removeCallbacks(navigateRunnable)
super.onDestroy()
}
private companion object {
const val SPLASH_DELAY_MILLIS = 2_000L
}
}

View File

@@ -1,18 +0,0 @@
package com.juhabi.birds.login.model
import com.google.gson.annotations.SerializedName
data class LoginUser(
val scope: Any? = null,
val openid: Any? = null,
@SerializedName("access_token")
val accessToken: String = "",
@SerializedName("refresh_token")
val refreshToken: Any? = null,
@SerializedName("expire_in")
val expireIn: Long = 0,
@SerializedName("refresh_expire_in")
val refreshExpireIn: Any? = null,
@SerializedName("client_id")
val clientId: String = ""
)

View File

@@ -0,0 +1,7 @@
package com.juhabi.birds.login.model
data class SendCodeEntity(
val code: Int,
val `data`: Any,
val msg: String
)

View File

@@ -0,0 +1,11 @@
package com.juhabi.birds.login.model
import com.google.gson.annotations.SerializedName
data class UserEntity(
val scope: Any? = null,
val openid: Any? = null,
@SerializedName("access_token")
val token: String = "",
)

View File

@@ -1,7 +1,8 @@
package com.juhabi.birds.login.mvp
import com.juhabi.birds.base.IView
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
@@ -12,15 +13,38 @@ interface LoginContract {
}
interface DataModel {
fun login(json: JSONObject, callback: Callback<BaseBean<LoginUser>>)
//手机验证码
fun sendPhoneCode(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//手机号+验证码注册
fun registerByPhone(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//手机+密码登录
fun loginByPhonePsd(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//手机+验证码登录
fun loginByPhoneSms(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//手机号+验证码修改密码
fun forgetPsdByPhone(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱验证码
fun sendEmailCode(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//邮箱+验证码注册
fun registerByEmail(json: JSONObject, callback: Callback<BaseBean<SendCodeEntity>>)
//邮箱+密码登录
fun loginByEmailPsd(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱+验证码登录
fun loginByEmailSms(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
//邮箱+验证码修改密码
fun forgetPsdByEmail(json: JSONObject, callback: Callback<BaseBean<UserEntity>>)
fun cancel()
}
interface LoginView : IView {
fun sendCodeSuccess(message: String)
fun onRegisterSuccess(message: String)
fun onForgetPsdSuccess(message: String)
fun showUsernameError(message: String)
fun showPasswordError(message: String)
fun clearInputErrors()
fun onLoginSuccess(user: LoginUser)
fun onLoginSuccess(user: UserEntity)
}
}

View File

@@ -2,7 +2,8 @@ package com.juhabi.birds.login.mvp
import android.os.Handler
import android.os.Looper
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.ApiClient
import com.juhabi.birds.network.BaseBean
import com.juhabi.birds.network.NetworkManager
@@ -13,45 +14,330 @@ import retrofit2.Response
class LoginImpl : LoginContract.DataModel {
private val mainHandler = Handler(Looper.getMainLooper())
private var loginCall: Call<BaseBean<LoginUser>>? = null
private var loginCall: Call<BaseBean<UserEntity>>? = null
override fun login(
private val calls = mutableSetOf<Call<*>>()
/**
* 发送手机验证码
*/
override fun sendPhoneCode(json: JSONObject, callback: LoginContract.Callback<BaseBean<SendCodeEntity>>) {
enqueueSendCodeCall(
ApiClient.apiService.sendPhoneCode(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
/**
* 手机号注册
*/
override fun registerByPhone(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<LoginUser>>
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
enqueueSendCodeCall(
ApiClient.apiService.registerByPhone(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
private fun enqueueSendCodeCall(
call: Call<BaseBean<SendCodeEntity>>,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(
call: Call<BaseBean<SendCodeEntity>>,
error: Throwable
) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByPhonePsd(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.loginByPhonePsd(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun loginByPhoneSms(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.loginByPhoneSms(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun forgetPsdByPhone(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
enqueueLoginCall(
ApiClient.apiService.forgetPsdByPhone(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
),
callback
)
}
override fun sendEmailCode(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
val call = ApiClient.apiService.sendEmailCode(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<SendCodeEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun registerByEmail(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<SendCodeEntity>>
) {
val call = ApiClient.apiService.registerByE(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<SendCodeEntity>> {
override fun onResponse(
call: Call<BaseBean<SendCodeEntity>>,
response: Response<BaseBean<SendCodeEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<SendCodeEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByEmailPsd(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.loginByEmailPsd(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun loginByEmailSms(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.loginByEmailSms(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
override fun forgetPsdByEmail(
json: JSONObject,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
val call = ApiClient.apiService.forgetPsdByEmail(
NetworkManager.getToken(),
NetworkManager.toRequestBody(json)
)
calls += call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
calls -= call
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
calls -= call
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
}
})
}
private fun enqueueLoginCall(
call: Call<BaseBean<UserEntity>>,
callback: LoginContract.Callback<BaseBean<UserEntity>>
) {
loginCall?.cancel()
loginCall = ApiClient.apiService
.login(NetworkManager.getToken(), NetworkManager.toRequestBody(json))
.also { call ->
call.enqueue(object : Callback<BaseBean<LoginUser>> {
override fun onResponse(
call: Call<BaseBean<LoginUser>>,
response: Response<BaseBean<LoginUser>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
loginCall = call
call.enqueue(object : Callback<BaseBean<UserEntity>> {
override fun onResponse(
call: Call<BaseBean<UserEntity>>,
response: Response<BaseBean<UserEntity>>
) {
mainHandler.post {
val bean = response.body()
if (response.isSuccessful && bean != null) {
callback.success(bean)
} else {
callback.fail("接口响应异常:${response.code()}")
}
}
}
override fun onFailure(call: Call<BaseBean<LoginUser>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
override fun onFailure(call: Call<BaseBean<UserEntity>>, error: Throwable) {
if (!call.isCanceled) {
mainHandler.post {
callback.fail(error.message ?: "网络请求失败")
}
}
})
}
}
})
}
override fun cancel() {
loginCall?.cancel()
loginCall = null
calls.forEach { it.cancel() }
calls.clear()
mainHandler.removeCallbacksAndMessages(null)
}
}

View File

@@ -1,44 +1,31 @@
package com.juhabi.birds.login.mvp
import com.juhabi.birds.base.BasePresenter
import com.juhabi.birds.AppConfig
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.network.BaseBean
import org.json.JSONObject
class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginContract.LoginView>() {
fun login(username: String, password: String) {
val currentView = view ?: return
currentView.clearInputErrors()
if (username.isBlank()) {
currentView.showUsernameError("请输入账号")
return
}
if (password.isBlank()) {
currentView.showPasswordError("请输入密码")
return
}
currentView.showLoading()
fun getPhoneSmsCode(phoneNumber: String, sendCodeSceneRegister: String) {
val json = JSONObject();
json.put("grantType", "password")
json.put("username", username.trim())
json.put("password", password)
json.put("clientId", AppConfig.APP_CLIENT_ID)
json.put("userType", AppConfig.APP_USER_TYPE)
loginImpl.login(
json.put("phoneNumber", phoneNumber)
json.put("scene", sendCodeSceneRegister)
loginImpl.sendPhoneCode(
json,
object : LoginContract.Callback<BaseBean<LoginUser>> {
override fun success(data: BaseBean<LoginUser>) {
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
attachedView.onLoginSuccess(user)
// 发码接口通常只返回 code/msg,data 可能为 null,不能依赖 data 非空
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
)
} else {
attachedView.showError(data.message.ifBlank { "登录失败" })
attachedView.showError(data.message.ifBlank { "服务异常" })
}
}
@@ -52,8 +39,310 @@ class LoginPresenter(private val loginImpl: LoginImpl) : BasePresenter<LoginCont
)
}
/**
* 手机 + 密码登录
* 入参:phoneNumber、password
*/
fun loginByPhonePsd(phoneNumber: String, password: String) {
val currentView = view ?: return
currentView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("password", password)
loginImpl.loginByPhonePsd(
json,
createLoginCallback()
)
}
/**
* 手机 + 验证码登录
* 入参:phoneNumber、code
*/
fun loginByPhoneSms(phoneNumber: String, code: String) {
val currentView = view ?: return
currentView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("code", code.trim())
loginImpl.loginByPhoneSms(
json,
createLoginCallback()
)
}
private fun createLoginCallback(): LoginContract.Callback<BaseBean<UserEntity>> {
return object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
attachedView.onLoginSuccess(user)
} else {
attachedView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
}
override fun onDestroy() {
loginImpl.cancel()
super.onDestroy()
}
/**
* 手机号注册
*/
fun registerByPhone(phone: String, code: String, password: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phone.trim())
json.put("code", code.trim())
json.put("password", password)
loginImpl.registerByPhone(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 手机号 + 验证码修改密码
* 入参:phoneNumber、code、newPassword
*/
fun forgetPsdByPhone(phoneNumber: String, code: String, newPassword: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("phoneNumber", phoneNumber.trim())
json.put("code", code.trim())
json.put("newPassword", newPassword)
loginImpl.forgetPsdByPhone(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
// 重置密码接口 data 可能为空,以 code 判断成功
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 获取邮箱验证码
* 入参:email、scene
*/
fun getEmailSmsCode(email: String, scene: String) {
val json = JSONObject()
json.put("email", email.trim())
json.put("scene", scene)
loginImpl.sendEmailCode(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val attachedView = view ?: return
attachedView.hideLoading()
if (data.code == 0 || data.code == 200) {
attachedView.sendCodeSuccess(
data.message.ifBlank { "验证码已发送" }
)
} else {
attachedView.showError(data.message.ifBlank { "服务异常" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱注册
* 入参:email、code、password、nickName
*/
fun registerByEmail(email: String, code: String, password: String, nickName: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
json.put("password", password)
json.put("nickName", nickName.trim())
loginImpl.registerByEmail(
json,
object : LoginContract.Callback<BaseBean<SendCodeEntity>> {
override fun success(data: BaseBean<SendCodeEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onRegisterSuccess(
data.message.ifBlank { "注册成功" }
)
} else {
currentView.showError(data.message.ifBlank { "注册失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 密码登录
* 入参:email、password
*/
fun loginByEmailPsd(email: String, password: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("password", password)
loginImpl.loginByEmailPsd(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 验证码登录
* 入参:email、code
*/
fun loginByEmailSms(email: String, code: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
loginImpl.loginByEmailSms(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
val user = data.data
if ((data.code == 0 || data.code == 200) && user != null) {
currentView.onLoginSuccess(user)
} else {
currentView.showError(data.message.ifBlank { "登录失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
/**
* 邮箱 + 验证码修改密码
* 入参:email、code、newPassword
*/
fun forgetPsdByEmail(email: String, code: String, newPassword: String) {
val attachedView = view ?: return
attachedView.showLoading()
val json = JSONObject()
json.put("email", email.trim())
json.put("code", code.trim())
json.put("newPassword", newPassword)
loginImpl.forgetPsdByEmail(
json,
object : LoginContract.Callback<BaseBean<UserEntity>> {
override fun success(data: BaseBean<UserEntity>) {
val currentView = view ?: return
currentView.hideLoading()
if (data.code == 0 || data.code == 200) {
currentView.onForgetPsdSuccess(
data.message.ifBlank { "密码修改成功" }
)
} else {
currentView.showError(data.message.ifBlank { "密码修改失败" })
}
}
override fun fail(message: String) {
view?.apply {
hideLoading()
showError(message)
}
}
}
)
}
}

View File

@@ -1,6 +1,7 @@
package com.juhabi.birds.network
import com.juhabi.birds.login.model.LoginUser
import com.juhabi.birds.login.model.UserEntity
import com.juhabi.birds.login.model.SendCodeEntity
import com.juhabi.birds.main.model.AppVersionInfo
import com.juhabi.birds.mine.model.MineUserInfo
import okhttp3.RequestBody
@@ -15,8 +16,105 @@ import retrofit2.http.POST
* 项目统一接口定义。所有业务接口地址集中维护在这里。
*/
interface ApiService {
@POST("auth/login")
fun login(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<LoginUser>>
//发送手机验证码
@POST("app/auth/sms/code")
fun sendPhoneCode(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//手机号注册
@POST("app/auth/register")
fun registerByPhone(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//手机+密码登录
/** 入参
* {
* "phoneNumber": "18764689178",
* "password": "Zz123456"
* }
*/
@POST("app/auth/login")
fun loginByPhonePsd(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//手机+验证码
/**入参
* {
* "phoneNumber": "string",
* "code": "string"
* }
*/
@POST("app/auth/sms-login")
fun loginByPhoneSms(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//手机号+验证码 修改密码
/**
* {
* "phoneNumber": "string",
* "code": "string",
* "newPassword": "string"
* }
*/
@POST("app/auth/reset-pwd")
fun forgetPsdByPhone(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//发送邮箱验证码
/**
* {
* "email": "string",
* "scene": "string"
* }
*/
@POST("app/auth/email/code")
fun sendEmailCode(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//邮箱注册
/**
* {
* "email": "string",
* "code": "string",
* "password": "string",
* "nickName": "string"
* }
*/
@POST("app/auth/email/register")
fun registerByE(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<SendCodeEntity>>
//邮箱+密码登录
/** 入参
* {
* "email": "string",
* "password": "string"
* }
*/
@POST("app/auth/email/login")
fun loginByEmailPsd(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//邮箱+验证码登录
/**入参
{
"email": "string",
"code": "string"
}
*/
@POST("app/auth/email/code-login")
fun loginByEmailSms(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
//邮箱+验证码 修改密码
/**
* {
* "email": "string",
* "code": "string",
* "newPassword": "string"
* }
*/
@POST("app/auth/email/reset-pwd")
fun forgetPsdByEmail(@HeaderMap headers: Map<String, String>, @Body requestBody: RequestBody): Call<BaseBean<UserEntity>>
@GET("app/v1/getUserInfo")
fun getMineUserInfo(@HeaderMap headers: Map<String, String>, @QueryMap params: Map<String, String>): Call<BaseBean<MineUserInfo>>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_zhu"
android:gravity="center_horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="210dp"
android:src="@mipmap/icon_app_logo"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:textColor="@color/white"
android:textSize="24sp"
android:textStyle="bold"
android:text="@string/app_chinese_name"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:gravity="center"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold"
android:text="@string/app_name"/>
</LinearLayout>

View File

@@ -1,88 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F5F7FA"
android:padding="24dp">
android:background="@color/color_zhu"
android:fillViewport="true"
android:overScrollMode="never">
<LinearLayout
android:id="@+id/loginContent"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.38">
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingBottom="32dp">
<TextView
<ImageView
android:id="@+id/brandLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/login_title"
android:textColor="#18212F"
android:textSize="28sp"
android:textStyle="bold" />
android:layout_gravity="center_horizontal"
android:layout_marginTop="110dp"
android:contentDescription="@string/app_chinese_name"
android:src="@mipmap/icon_app_logo" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/login_subtitle"
android:textColor="#667085"
android:textSize="15sp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:hint="@string/login_username">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/usernameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="phone"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:id="@+id/brandName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:hint="@string/login_password"
app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/passwordEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="24dp"
android:text="@string/login_button"
android:textSize="16sp" />
android:gravity="center"
android:text="@string/app_chinese_name"
android:textColor="@color/color_white"
android:textSize="23sp"
android:textStyle="bold" />
<TextView
android:id="@+id/resultTextView"
android:id="@+id/brandSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/login_demo_hint"
android:textColor="#667085"
android:text="@string/app_name"
android:textColor="@color/color_DDE6F5"
android:textSize="14sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="54dp"
android:background="@drawable/shape_login_field_bg"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="22dp"
android:paddingEnd="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@mipmap/icon_login_phone" />
<EditText
android:id="@+id/accountEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="@null"
android:hint="@string/register_account_hint"
android:inputType="text"
android:maxLines="1"
android:textColor="@color/color_2E2E2E"
android:textColorHint="@color/color_999999"
android:textSize="15sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear_psd"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:background="@drawable/shape_login_field_bg"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="22dp"
android:paddingEnd="16dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@null"
android:src="@mipmap/icon_login_code" />
<EditText
android:id="@+id/passwordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:background="@null"
android:hint="@string/register_password_hint"
android:inputType="textPassword"
android:maxLines="1"
android:textColor="@color/color_2E2E2E"
android:textColorHint="@color/color_999999"
android:textSize="15sp" />
<ImageView
android:id="@+id/passwordVisibilityButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:contentDescription="@string/register_show_password"
android:src="@mipmap/icon_login_show" />
</LinearLayout>
<LinearLayout
android:id="@+id/linear_code"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="14dp"
android:paddingEnd="10dp"
android:visibility="gone">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_code"
android:contentDescription="@null" />
<EditText
android:id="@+id/codeEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
android:hint="请输入验证码"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<TextView
android:id="@+id/getCodeButton"
android:layout_width="100dp"
android:layout_height="38dp"
android:background="@drawable/shape_register_code_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
android:text="获取验证码"
android:textAllCaps="false"
android:textColor="@color/color_white"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/tvChangeCode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="end"
android:layout_marginEnd="20dp"
android:layout_marginStart="20dp"
android:text="@string/register_change_code_login"
android:textColor="@color/color_DDE6F5"
android:textSize="13sp" />
<TextView
android:id="@+id/forgotPasswordButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="20dp"
android:text="@string/register_forgot_password"
android:textColor="@color/color_DDE6F5"
android:textSize="13sp" />
</LinearLayout>
<TextView
android:id="@+id/loginButton"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="15dp"
android:layout_marginTop="78dp"
android:layout_marginEnd="15dp"
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
android:text="@string/login_button"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="16sp" />
<TextView
android:id="@+id/agreementText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:text="@string/register_agreement"
android:textColor="@color/color_DDE6F5"
android:textSize="12sp" />
<TextView
android:id="@+id/registerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="54dp"
android:text="@string/register_prompt"
android:textColor="@color/color_DDE6F5"
android:textSize="14sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

View File

@@ -0,0 +1,277 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/color_zhu">
<View
android:id="@+id/statusBarView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/color_zhu" />
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="48dp">
<include
android:id="@+id/included_title"
layout="@layout/include_activity_back_gray_layout" />
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="57dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="14dp">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_phone"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/areaCodeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="+86"
android:textColor="@color/color_2E2E2E"
android:textSize="14sp" />
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="2dp"
android:src="@mipmap/icon_login_point_down"/>
</LinearLayout>
<EditText
android:id="@+id/phoneEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请输入手机号"
android:inputType="phone"
android:layout_marginStart="8dp"
android:maxLines="1"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="14dp"
android:paddingEnd="10dp">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_code"
android:contentDescription="@null" />
<EditText
android:id="@+id/codeEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
android:hint="请输入验证码"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<TextView
android:id="@+id/getCodeButton"
android:layout_width="100dp"
android:layout_height="38dp"
android:background="@drawable/shape_register_code_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
android:text="获取验证码"
android:textAllCaps="false"
android:textColor="@color/color_white"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="44dp"
android:paddingEnd="16dp">
<EditText
android:id="@+id/passwordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请输入8-20位密码"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<ImageView
android:id="@+id/passwordVisibilityButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
android:contentDescription="显示密码" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="44dp"
android:paddingEnd="16dp">
<EditText
android:id="@+id/confirmPasswordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请再次输入新密码"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<ImageView
android:id="@+id/confirmPasswordVisibilityButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
android:contentDescription="显示密码" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="26dp"
android:layout_marginEnd="26dp"
android:layout_marginTop="36dp"
android:orientation="vertical"
android:paddingStart="18dp"
android:paddingVertical="10dp"
android:background="@drawable/shape_register_rule_bg">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码规则"
android:textColor="@color/color_white"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginVertical="5dp"
android:src="@mipmap/icon_login_select"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="长度在8-20个字符之间"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginVertical="5dp"
android:src="@mipmap/icon_login_select"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="包含大小写字母和数字"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginVertical="5dp"
android:src="@mipmap/icon_login_tips"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="不能与当前密码相同"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/confirmButton"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginTop="84dp"
android:gravity="center"
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:text="确定"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:id="@+id/relative_title"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/color_zhu">
<ImageView
android:id="@+id/img_back"
android:layout_width="22dp"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:src="@mipmap/img_back_black"
android:layout_marginStart="10dp"/>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:textColor="@color/white"
android:textStyle="bold"
android:text=""
android:layout_marginStart="10dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
<TextView
android:id="@+id/tv_title_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toRightOf="@+id/tv_title"
android:textSize="14sp"
android:textColor="@color/color_2E2E2E"
android:text="(剩余时间)"
android:layout_marginStart="5dp"
android:visibility="gone"/>
<TextView
android:id="@+id/tv_title_right"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_marginRight="10dp"
android:textSize="14sp"
android:textColor="@color/color_333333"
android:text=""
android:layout_marginStart="5dp"
android:visibility="visible"/>
<ImageView
android:id="@+id/img_setting"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:src="@mipmap/ic_launcher"
android:layout_marginEnd="20dp"
android:visibility="gone"/>
</RelativeLayout>
</RelativeLayout>

View File

@@ -0,0 +1,258 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/color_zhu">
<View
android:id="@+id/statusBarView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/color_zhu" />
<RelativeLayout
android:id="@+id/rl_title"
android:layout_width="match_parent"
android:layout_height="48dp">
<include
android:id="@+id/included_title"
layout="@layout/include_activity_back_gray_layout" />
</RelativeLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:paddingBottom="32dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="57dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="14dp">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_phone"
android:contentDescription="@null" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/areaCodeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="+86"
android:textColor="@color/color_2E2E2E"
android:textSize="14sp" />
<ImageView
android:layout_width="10dp"
android:layout_height="10dp"
android:layout_marginStart="2dp"
android:src="@mipmap/icon_login_point_down"/>
</LinearLayout>
<EditText
android:id="@+id/phoneEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请输入手机号"
android:inputType="phone"
android:layout_marginStart="8dp"
android:maxLines="1"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="14dp"
android:paddingEnd="10dp">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@mipmap/icon_login_code"
android:contentDescription="@null" />
<EditText
android:id="@+id/codeEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginStart="8dp"
android:background="@null"
android:hint="请输入验证码"
android:inputType="number"
android:maxLines="1"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<TextView
android:id="@+id/getCodeButton"
android:layout_width="100dp"
android:layout_height="38dp"
android:background="@drawable/shape_register_code_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:gravity="center"
android:text="@string/register_get_code"
android:textAllCaps="false"
android:textColor="@color/color_white"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="44dp"
android:paddingEnd="16dp">
<EditText
android:id="@+id/passwordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请输入8-20位密码"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<ImageView
android:id="@+id/passwordVisibilityButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
android:contentDescription="显示密码" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:background="@drawable/shape_login_field_bg"
android:paddingStart="44dp"
android:paddingEnd="16dp">
<EditText
android:id="@+id/confirmPasswordEditText"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:hint="请再次输入新密码"
android:inputType="textPassword"
android:textColorHint="@color/color_999999"
android:textSize="16sp" />
<ImageView
android:id="@+id/confirmPasswordVisibilityButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/icon_login_show"
android:contentDescription="显示密码" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="98dp"
android:layout_marginStart="26dp"
android:layout_marginEnd="26dp"
android:layout_marginTop="36dp"
android:orientation="vertical"
android:paddingStart="18dp"
android:paddingTop="10dp"
android:background="@drawable/shape_register_rule_bg">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码规则"
android:textColor="@color/color_white"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginVertical="5dp"
android:src="@mipmap/icon_login_select"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="长度在8-20个字符之间"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_marginVertical="5dp"
android:src="@mipmap/icon_login_select"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="包含大小写字母和数字"
android:textColor="@color/white"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/confirmButton"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_marginTop="84dp"
android:gravity="center"
android:background="@drawable/shape_login_field_bg"
android:backgroundTint="@null"
android:stateListAnimator="@null"
android:text="确定"
android:textAllCaps="false"
android:textColor="@color/color_zhu"
android:textSize="17sp" />
</LinearLayout>
</ScrollView>
</LinearLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 532 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -46,7 +46,7 @@
<!--视频准备页面,背景半透明颜色-->
<color name="color_1d76fe">#1D76FE</color>
<color name="color_1a72ff">#1A72FF</color>
<color name="color_zhu">#00CA9D</color>
<color name="color_zhu">#005431</color>
<color name="color_c8c8c8">#C8C8C8</color>
<color name="color_F6F6F6">#F6F6F6</color>
<color name="color_2E2E2E">#2E2E2E</color>
@@ -117,7 +117,6 @@
<color name="color_F0F8F1">#F0F8F1</color>
<color name="color_FFFCEB">#FFFCEB</color>
<color name="color_1D90FF">#1D90FF</color>
<color name="color_8000CA9D">#8000CA9D</color>
@@ -139,6 +138,7 @@
<color name="page_background">#F7F8FA</color>
<color name="bottom_nav_background">#FFFFFFFF</color>
<color name="plan_top_image_color">#C51609</color>
<color name="register_psd_rule">#286F51</color>
</resources>

View File

@@ -1,10 +1,52 @@
<resources>
<string name="app_name">KotMvpProject</string>
<string name="app_chinese_name">聚哈比‑萨克尔GPS智能隼眼罩</string>
<string name="app_name">JUMHABI Saqr GPS Smart Falcon Hood</string>
<string name="login_title">账号登录</string>
<string name="login_subtitle">请输入账号和密码</string>
<string name="login_username">手机号</string>
<string name="login_password">密码</string>
<string name="login_button">登录</string>
<string name="login_register">手机注册</string>
<string name="register_account_hint">请输入账号</string>
<string name="register_password_hint">请输入密码</string>
<string name="register_show_password">显示密码</string>
<string name="register_forgot_password">忘记密码?</string>
<string name="forget_password_title">忘记密码</string>
<string name="register_change_code_login">切换手机号验证码登录</string>
<string name="register_change_psd_login">切换密码登录</string>
<string name="register_agreement">我已阅读并同意用户协议和隐私政策以及定制条款</string>
<string name="register_prompt">没有账号?立即注册</string>
<string name="register_get_code">获取验证码</string>
<string name="register_code_countdown">%1$d秒后重新获取</string>
<string name="login_demo_hint">登录成功后将自动保存 Token</string>
<string name="mine_open_login">前往登录页面</string>
<string name="plan_top_image_description">计划页顶部背景图</string>