yijianlianUI/app/src/main/java/com/yingmao/clash/act/LoginActivity.kt

616 lines
26 KiB
Kotlin

package com.yingmao.clash.act
import android.annotation.SuppressLint
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.TextUtils
import android.text.method.HideReturnsTransformationMethod
import android.text.method.LinkMovementMethod
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.*
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.yingmao.clash.view.dialog.RxDialogShapeLoading
import com.yingmao.clash.net.http.entity.LoginRsp
import com.tencent.mmkv.MMKV
import com.yingmao.clash.BuildConfig
import com.yingmao.clash.R
import com.yingmao.clash.app.MainApplication
import com.yingmao.clash.base.BaseActivity
import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.entity.CheckLoginRsp
import com.yingmao.clash.listener.DownLoadListener
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
import com.yingmao.clash.util.AESUtil
import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.view.PasswordTransformationMethodBigDot
import com.yingmao.clash.view.RxToast
import com.yingmao.clash.view.dialog.DialogAppVersion
import com.yingmao.clash.view.dialog.DialogDoMainChoose
import com.yingmao.clash.vm.LoginAtyVM
import com.yingmao.clash.vm.SplashAtyVM
import com.yingmao.clash.webview.CustomerServiceWebView
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.io.File
import androidx.core.graphics.toColorInt
import androidx.core.net.toUri
import com.google.android.material.floatingactionbutton.FloatingActionButton
class LoginActivity : BaseActivity(), View.OnClickListener {
private lateinit var loginViewMode: LoginAtyVM
private lateinit var splashAtyVM: SplashAtyVM
private lateinit var usernameEt: EditText
private lateinit var passwordEt: EditText
private lateinit var rxDialogShapeLoading: RxDialogShapeLoading
private lateinit var rememberPasswordCB: CheckBox
private lateinit var userAgreementCB: CheckBox
private lateinit var loginUserName: String
private lateinit var tvRegister: TextView
private lateinit var customService: FloatingActionButton
private var usernameEncrypt = ""
private var passwordEncrypt = ""
private var userOAID = ""
private var registerHost = ""
companion object {
private const val LOGIN_TO_REGISTER_REQUEST_CODE = 1
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.layout_activity_login)
//点击输入框外边隐藏输入法 必须在setContentView之后调用
setupUI(findViewById<ViewGroup>(android.R.id.content).getChildAt(0))
loginViewMode = ViewModelProvider(this)[LoginAtyVM::class.java]
splashAtyVM = ViewModelProvider(this)[SplashAtyVM::class.java]
observe(this, loginViewMode)
initViews()
loginViewMode.upgradeVersion()
}
override fun httpLoading(httpStatus: HttpStatus.Loading) {
super.httpLoading(httpStatus)
if (httpStatus.reqType == HttpReqType.LOGIN) {
showLoadingDialog(resources.getString(R.string.LoggingIn))
} else if (httpStatus.reqType == HttpReqType.CHECK_LOGIN) {
showLoadingDialog(resources.getString(R.string.LoggingIn))
}
}
private fun showLoadingDialog(content: String) {
if (!::rxDialogShapeLoading.isInitialized) {
rxDialogShapeLoading = RxDialogShapeLoading(this)
}
rxDialogShapeLoading.setMessage(content)
if (!rxDialogShapeLoading.isShowing) {
rxDialogShapeLoading.show()
}
}
private fun hideLoadingDialog() {
if (::rxDialogShapeLoading.isInitialized && rxDialogShapeLoading.isShowing) {
rxDialogShapeLoading.cancel()
}
}
override fun httpSuccess(httpStatus: HttpStatus.Success) {
super.httpSuccess(httpStatus)
if (httpStatus.reqType == HttpReqType.LOGIN) {
val rsp = httpStatus.rsp
com.cncat.vpn.common.log.Log.d("HttpReqType.LOGIN rsp==>${rsp}")
if (rsp is LoginRsp) {
if (rsp.code == "1000") {
val mmkv = MMKV.defaultMMKV()
mmkv.encode(BZYConstants.MMKV_KEY_TOKEN, rsp.data.vpnToken)
mmkv.encode(BZYConstants.MMKV_KEY_PH_TOKEN, rsp.data.phToken)
mmkv.encode(BZYConstants.MMKV_KEY_USER_ID, rsp.data.userId)
mmkv.encode(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, loginUserName)
mmkv.encode(BZYConstants.MMKV_KEY_IS_REGISTER, true) //如果登录过一次也算注册过
loginViewMode.checkLogin()
} else {
hideLoadingDialog()
RxToast.error(
"(${rsp.code})${if (rsp.message.isNullOrEmpty()) resources.getString(R.string.LoginFailed) else rsp.message}",
Toast.LENGTH_LONG
)
}
}
} else if (httpStatus.reqType == HttpReqType.CHECK_LOGIN) {
hideLoadingDialog()
val rsp = httpStatus.rsp
if (rsp is CheckLoginRsp) {
if (rsp.code == 0) {
rsp.data?.let { MainApplication.setUserInfo(it) }
val expired = rsp.data?.expired
val isPerfect = rsp.data?.isPerfect
val userCommand = rsp.data?.userCommand
val mmkv = MMKV.defaultMMKV()
mmkv.encode(BZYConstants.MMKV_KEY_VPN_EXPIRED, expired ?: true)
mmkv.encode(
BZYConstants.MMKV_KEY_VPN_VALID_PERIOD,
rsp.data?.vpnExpireTime ?: 0
)
mmkv.encode(BZYConstants.MMKV_KEY_IS_PERFECT, isPerfect ?: true)
mmkv.encode(BZYConstants.MMKV_KEY_VPN_UserCommand, userCommand ?: "")
toMain()
} else {
hideLoadingDialog()
RxToast.error(
"(${rsp.code})${if (rsp.msg.isNullOrEmpty()) resources.getString(R.string.CheckLoginFailure) else rsp.msg}",
Toast.LENGTH_LONG
)
}
}
} else if (httpStatus.reqType == HttpReqType.GETAPPVERION) {
(httpStatus.rsp as AppVersionCheckRsp).let { aver ->
com.cncat.vpn.common.log.Log.i("appVersionCheck ==>$aver")
if (aver.code == 200) {
aver.data?.let { md ->
if (md.isForce) { //强制更新
if (!TextUtils.isEmpty(md.versionPath)) {
showUpdateDialog(md.versionPath!!, md.illustrate!!, true)
}
} else if (md.isUpgrade) { //更新
if (!TextUtils.isEmpty(md.versionPath)) {
showUpdateDialog(md.versionPath!!, md.illustrate!!, false)
}
}
}
}
}
}
}
var dialogDoMainChoose: DialogDoMainChoose? = null
override fun httpFailure(httpStatus: HttpStatus.Failure) {
super.httpFailure(httpStatus)
if (httpStatus.reqType == HttpReqType.LOGIN) {
SplashHelper(splashAtyVM).getHostWithFallback { success ->
MainScope().launch {
if (success) {
if (dialogDoMainChoose == null) {
dialogDoMainChoose = DialogDoMainChoose(this@LoginActivity)
}
if (dialogDoMainChoose?.isShowing == false) {
dialogDoMainChoose?.setOnTryButtonListener(object :
DialogDoMainChoose.OnTryButtonListener {
override fun tryButton() {
MMKV.defaultMMKV()
.decodeString(BZYConstants.MMKV_KEY_CURRENT_HOST, "")
loginViewMode.login(
usernameEncrypt,
passwordEncrypt,
userOAID
)
}
override fun cancel() {
com.cncat.vpn.common.log.Log.d("tryButton CHECK_LOGIN cancel")
dialogDoMainChoose?.dismiss()
}
})
dialogDoMainChoose?.setOnListItemListener(object :
DialogDoMainChoose.OnListItemListener {
override fun listItemClick() {
loginViewMode.login(
usernameEncrypt,
passwordEncrypt,
userOAID
)
}
})
dialogDoMainChoose?.show()
}
} else {
if (::rxDialogShapeLoading.isInitialized && rxDialogShapeLoading.isShowing) {
rxDialogShapeLoading.cancel()
}
RxToast.error(resources.getString(R.string.LoginException))
}
}
}
} else if (httpStatus.reqType == HttpReqType.CHECK_LOGIN) {
if (::rxDialogShapeLoading.isInitialized && rxDialogShapeLoading.isShowing) {
rxDialogShapeLoading.cancel()
}
Log.e(
"LoginActivity",
resources.getString(R.string.LoginException) + httpStatus.msg
)
RxToast.error(resources.getString(R.string.LoginException))
}
}
private fun toMain() {
startActivity(Intent(this, MainActivity::class.java))
finish()
}
private fun initViews() {
val bindAccount = intent.getStringExtra(BZYConstants.EXTRA_Bind_Account);
var username = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
if (!TextUtils.isEmpty(bindAccount)) {
username = bindAccount
}
val password = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_PASSWORD)
usernameEt = findViewById(R.id.et_username)
usernameEt.setText(username)
passwordEt = findViewById(R.id.et_password)
passwordEt.transformationMethod = PasswordTransformationMethodBigDot()
passwordEt.setText(password)
val isChecked =
MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_REMEMBER_IS_CHECKED, true)
rememberPasswordCB = findViewById(R.id.cb_remember_password)
if (!isChecked) {
rememberPasswordCB.setTextColor("#828282".toColorInt())
} else {
rememberPasswordCB.setTextColor("#828282".toColorInt())
}
rememberPasswordCB.isChecked = isChecked
rememberPasswordCB.setOnCheckedChangeListener { _, isChecked ->
if (!isChecked) {
MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_REMEMBER_PASSWORD)
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_IS_CHECKED, false)
rememberPasswordCB.setTextColor("#4F4F4F".toColorInt())
} else {
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_IS_CHECKED, true)
rememberPasswordCB.setTextColor("#4F4F4F".toColorInt())
}
}
val userAgreementIsChecked =
MMKV.defaultMMKV().decodeBool(BZYConstants.MMKV_KEY_USER_AGREEMENT_IS_CHECKED)
userAgreementCB = findViewById(R.id.cb_user_agreement)
if (!userAgreementIsChecked) {
userAgreementCB.setTextColor("#828282".toColorInt())
} else {
userAgreementCB.setTextColor("#4F4F4F".toColorInt())
}
userAgreementCB.isChecked = userAgreementIsChecked
userAgreementCB.setOnCheckedChangeListener { _, isChecked ->
if (!isChecked) {
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_USER_AGREEMENT_IS_CHECKED, false)
userAgreementCB.setTextColor("#828282".toColorInt())
} else {
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_USER_AGREEMENT_IS_CHECKED, true)
userAgreementCB.setTextColor("#4F4F4F".toColorInt())
}
}
initUserAgreementView(userAgreementCB)
val passwordHideCb = findViewById<CheckBox>(R.id.cb_password_hide)
passwordHideCb.setOnCheckedChangeListener(IOnCheckedChangeListener())
val loginTv = findViewById<TextView>(R.id.tv_login)
loginTv.setOnClickListener(this)
val registerTv = findViewById<TextView>(R.id.tv_register)
registerTv.setOnClickListener(this)
val forgetPwdTv = findViewById<TextView>(R.id.tv_forget_pwd)
forgetPwdTv.setOnClickListener(this)
findViewById<TextView>(R.id.tv_kefu).setOnClickListener(this)
tvRegister = findViewById(R.id.tvRegister)
tvRegister.setOnClickListener {
val intent = Intent(applicationContext, RegisterActivity::class.java)
startActivity(intent)
finish()
}
customService=findViewById(R.id.customService)
customService.setOnClickListener {
startActivity(Intent(this@LoginActivity, CustomerServiceWebView::class.java))
}
}
private fun initUserAgreementView(userAgreementCB: CheckBox) {
val config = resources.configuration
val language = config.locale.language
if (language.equals("en")) {
val spannableStringBuilder =
SpannableStringBuilder(resources.getString(R.string.AgreeAgreement))
val clickableSpan: MyClickableSpan = object : MyClickableSpan() {
override fun onClick(view: View) {
spannableStringBuilder.setSpan(
null,
5,
24,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.isChecked=true
val intent = Intent(this@LoginActivity, AgreementAndPrivacyActivity::class.java)
intent.putExtra(
BZYConstants.EXTRA_KEY_AGREEMENT,
BZYConstants.EXTRA_PRIVACY_AGREEMENT
)
startActivity(intent)
}
}
spannableStringBuilder.setSpan(clickableSpan, 5, 24, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
val privacyClickableSpan: MyClickableSpan = object : MyClickableSpan() {
override fun onClick(view: View) {
spannableStringBuilder.setSpan(
null,
27,
resources.getString(R.string.AgreeAgreement).length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.isChecked=true
val intent = Intent(this@LoginActivity, AgreementAndPrivacyActivity::class.java)
intent.putExtra(
BZYConstants.EXTRA_KEY_AGREEMENT,
BZYConstants.EXTRA_USER_AGREEMENT
)
startActivity(intent)
}
}
spannableStringBuilder.setSpan(
privacyClickableSpan,
27,
resources.getString(R.string.AgreeAgreement).length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.movementMethod = LinkMovementMethod.getInstance()
} else {
val spannableStringBuilder =
SpannableStringBuilder(resources.getString(R.string.AgreeAgreement))
val clickableSpan: MyClickableSpan = object : MyClickableSpan() {
override fun onClick(view: View) {
spannableStringBuilder.setSpan(
null,
8,
13,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.isChecked=true
val intent = Intent(this@LoginActivity, AgreementAndPrivacyActivity::class.java)
intent.putExtra(
BZYConstants.EXTRA_KEY_AGREEMENT,
BZYConstants.EXTRA_PRIVACY_AGREEMENT
)
startActivity(intent)
}
}
spannableStringBuilder.setSpan(clickableSpan, 8, 13, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
val privacyClickableSpan: MyClickableSpan = object : MyClickableSpan() {
override fun onClick(view: View) {
spannableStringBuilder.setSpan(
null,
14,
resources.getString(R.string.AgreeAgreement).length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.isChecked=true
val intent = Intent(this@LoginActivity, AgreementAndPrivacyActivity::class.java)
intent.putExtra(
BZYConstants.EXTRA_KEY_AGREEMENT,
BZYConstants.EXTRA_USER_AGREEMENT
)
startActivity(intent)
}
}
spannableStringBuilder.setSpan(
privacyClickableSpan,
14,
resources.getString(R.string.AgreeAgreement).length,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE
)
userAgreementCB.text = spannableStringBuilder
userAgreementCB.movementMethod = LinkMovementMethod.getInstance()
}
}
override fun onClick(v: View?) {
if (v != null) {
when (v.id) {
R.id.tv_login -> {
val username = usernameEt.text.toString().trim()
if (username.isBlank()) {
getFocusAndShowSoftInput(usernameEt)
return
}
if (!CommonUtils.checkUsername(username)) {
getFocusAndShowSoftInput(usernameEt)
return
}
val password = passwordEt.text.toString().trim()
if (password.isBlank()) {
getFocusAndShowSoftInput(passwordEt)
return
}
if (rememberPasswordCB.isChecked) {
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, username)
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_PASSWORD, password)
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_IS_CHECKED, true)
} else {
// MMKV.defaultMMKV().removeValueForKey(BZYConstants.MMKV_KEY_REMEMBER_USERNAME)
MMKV.defaultMMKV()
.removeValueForKey(BZYConstants.MMKV_KEY_REMEMBER_PASSWORD)
MMKV.defaultMMKV().encode(BZYConstants.MMKV_KEY_REMEMBER_IS_CHECKED, false)
}
if (TextUtils.isEmpty(username)) {
return
}
usernameEncrypt = AESUtil.byteArrayToHexStr(
AESUtil.encrypt(username)
)
passwordEncrypt = AESUtil.byteArrayToHexStr(
AESUtil.encrypt(password)
)
Log.d("OALD", "OAID.....::" + MainApplication.OAID)
userOAID = AESUtil.byteArrayToHexStr(
AESUtil.encrypt(MainApplication.OAID)
)
loginUserName = username
if (!userAgreementCB.isChecked) {
RxToast.warning(resources.getString(R.string.ReadAgreement))
return
}
loginViewMode.login(usernameEncrypt, passwordEncrypt, userOAID)
}
R.id.tv_register -> {
if (BuildConfig.is_free && BuildConfig.productId.isNotEmpty()) {
val intent = Intent(applicationContext, SplashActivity::class.java)
startActivity(intent)
} else {
if (TextUtils.isEmpty(registerHost)) {
val intent = Intent(applicationContext, RegisterActivity::class.java)
startActivity(intent)
finish()
} else {
startActivity(Intent(Intent.ACTION_VIEW, registerHost.toUri()))
}
}
}
R.id.tv_kefu -> {
startActivity(Intent(this@LoginActivity, CustomerServiceWebView::class.java))
}
R.id.tv_forget_pwd -> {
val intent = Intent(applicationContext, ForGetPwdActivity::class.java)
startActivity(intent)
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == LOGIN_TO_REGISTER_REQUEST_CODE) {
data?.let {
usernameEt.setText(it.extras?.getString("username"))
passwordEt.setText(it.extras?.getString("password"))
}
}
}
private fun getFocusAndShowSoftInput(editText: EditText) {
editText.requestFocus()
val imm: InputMethodManager = editText.context
.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED)
}
private inner class IOnCheckedChangeListener : CompoundButton.OnCheckedChangeListener {
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
if (isChecked) {
passwordEt.transformationMethod = HideReturnsTransformationMethod.getInstance()
passwordEt.setSelection(passwordEt.text.length)
} else {
passwordEt.transformationMethod = PasswordTransformationMethodBigDot()
passwordEt.setSelection(passwordEt.text.length)
}
}
}
private fun showUpdateDialog(url: String, illustrate: String, isForce: Boolean) {
if (TextUtils.isEmpty(url)) {
RxToast.error(resources.getString(R.string.downloadEmpty))
return
}
var notice = illustrate
if (TextUtils.isEmpty(illustrate)) {
notice = resources.getString(R.string.NewVersion)
}
var isDownloaded = false;
var isFinish = false
var downFilePath = ""
val dialog = DialogAppVersion(
this,
notice,
url,
isForce,
object : DialogAppVersion.OnClickListener {
override fun ok(tv_down: TextView, pb_down: ProgressBar) {
if (isFinish) {
installApk(downFilePath)
return
}
if (isDownloaded) {
RxToast.info(resources.getString(R.string.downloading))
return
}
isDownloaded = true
splashAtyVM.downLoadFile(url, object : DownLoadListener {
@SuppressLint("SetTextI18n")
override fun loading(progress: Int) {
lifecycleScope.async {
pb_down.progress = progress
tv_down.text = resources.getString(R.string.download) + "$progress%"
}
}
override fun finish(path: String) {
com.cncat.vpn.common.log.Log.d("down finish path==>$path")
isFinish = true
downFilePath = path
installApk(path)
}
override fun error(msg: String) {
com.cncat.vpn.common.log.Log.d("down error msg==>$msg")
}
})
}
override fun cancel() {
}
})
dialog.show()
}
fun installApk(filePath: String) {
val file = File(filePath)
val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(this, "${applicationContext.packageName}.fileprovider", file)
} else {
Uri.fromFile(file)
}
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "application/vnd.android.package-archive")
flags = Intent.FLAG_ACTIVITY_NEW_TASK
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
}
startActivity(intent)
}
}