commit 8c691b0f685491157e601950c0f45fca3667a8ae Author: maxinyu Date: Sat Apr 25 17:22:36 2026 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..ca16a99 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..02c4aa5 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6a84da8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..6c74be6 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + alias(libs.plugins.android.application) +} + +android { + namespace = "com.example.defenseapplication" + compileSdk { + version = release(36) { + minorApiLevel = 1 + } + } + + defaultConfig { + applicationId = "com.example.defenseapplication" + minSdk = 24 + targetSdk = 36 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + buildFeatures { + viewBinding=true + } + +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + implementation(libs.androidx.activity) + implementation(libs.androidx.constraintlayout) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + implementation("com.squareup.okhttp3:okhttp:4.12.0") + // 可选:协程扩展支持 + implementation("com.squareup.okhttp3:okhttp-coroutines:4.12.0") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/defenseapplication/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/defenseapplication/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..db36c1d --- /dev/null +++ b/app/src/androidTest/java/com/example/defenseapplication/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.defenseapplication + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.defenseapplication", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4b7d2ba --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/login/ForgetPasswordActivity.kt b/app/src/main/java/com/example/defenseapplication/login/ForgetPasswordActivity.kt new file mode 100644 index 0000000..d49b835 --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/login/ForgetPasswordActivity.kt @@ -0,0 +1,205 @@ +package com.example.defenseapplication.login + +import android.os.Bundle +import android.os.CountDownTimer +import android.text.InputType +import android.text.TextUtils +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import com.example.defenseapplication.R +import com.example.defenseapplication.databinding.ForgetPasswordActivityBinding +import com.example.defenseapplication.okhttp.OkHttpClientManager +import java.util.regex.Pattern +import kotlin.random.Random + +class ForgetPasswordActivity : AppCompatActivity() { + private lateinit var binding: ForgetPasswordActivityBinding + private var verifyCode: String? = null // 存储模拟验证码 + private var countDownTimer: CountDownTimer? = null + private var isSendingCode = false // 防止重复发送验证码 + + companion object { + private const val COUNTDOWN_DURATION = 60000L // 60秒倒计时 + private const val COUNTDOWN_INTERVAL = 1000L + private const val MIN_PASSWORD_LENGTH = 6 + private const val MAX_PASSWORD_LENGTH = 20 + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ForgetPasswordActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupClickListeners() + } + + private fun setupClickListeners() { + // 返回按钮 + binding.backIcon.setOnClickListener { + finish() + } + + // 发送验证码按钮 + binding.sendCodeBtn.setOnClickListener { + if (!isSendingCode && countDownTimer == null) { + val phoneNumber = binding.etPhoneNumber.text.toString().trim() + if (isPhoneNumberValid(phoneNumber)) { + sendVerificationCode(phoneNumber) + } else { + Toast.makeText(this, "请输入有效的11位手机号", Toast.LENGTH_SHORT).show() + } + } else { + Toast.makeText(this, "请稍后再试", Toast.LENGTH_SHORT).show() + } + } + + // 确定按钮(重置密码) + binding.btnNext.setOnClickListener { + performResetPassword() + } + } + + /** + * 手机号格式校验(简单1开头的11位数字) + */ + private fun isPhoneNumberValid(phone: String): Boolean { + if (TextUtils.isEmpty(phone)) return false + val pattern = Pattern.compile("^1[0-9]{10}$") + return pattern.matcher(phone).matches() + } + + /** + * 发送验证码 + */ + private fun sendVerificationCode(phoneNumber: String) { + isSendingCode = true + binding.sendCodeBtn.isEnabled = false + + OkHttpClientManager.postForm( + url = "https://httpbin.org/post", + params = mapOf("phone" to phoneNumber) + ) { result -> + result.onSuccess { + verifyCode = (100000 + Random.nextInt(900000)).toString() + startCountDown() + Toast.makeText(this, "验证码已发送(演示码:$verifyCode)", Toast.LENGTH_SHORT).show() + }.onFailure { + Toast.makeText(this@ForgetPasswordActivity, "发送失败,请重试", Toast.LENGTH_SHORT).show() + resetSendButton() + } + isSendingCode = false + } + } + + /** + * 开始验证码按钮倒计时 + */ + private fun startCountDown() { + countDownTimer = object : CountDownTimer(COUNTDOWN_DURATION, COUNTDOWN_INTERVAL) { + override fun onTick(millisUntilFinished: Long) { + val secondsRemaining = (millisUntilFinished / 1000).toInt() + binding.sendCodeBtn.text = "${secondsRemaining}秒后重试" + binding.sendCodeBtn.isEnabled = false + } + + override fun onFinish() { + resetSendButton() + } + }.start() + } + + /** + * 重置发送按钮状态 + */ + private fun resetSendButton() { + countDownTimer?.cancel() + countDownTimer = null + binding.sendCodeBtn.text = "发送验证码" + binding.sendCodeBtn.isEnabled = true + } + + /** + * 执行重置密码请求 + */ + private fun performResetPassword() { + val phoneNumber = binding.etPhoneNumber.text.toString().trim() + val code = binding.etSmsCode.text.toString().trim() + val newPassword = binding.etPassword.text.toString().trim() + val confirmPassword = binding.etInviteCode.text.toString().trim() + + // 1. 手机号校验 + if (!isPhoneNumberValid(phoneNumber)) { + Toast.makeText(this, "手机号格式不正确", Toast.LENGTH_SHORT).show() + return + } + + // 2. 验证码校验 + if (TextUtils.isEmpty(code)) { + Toast.makeText(this, "请输入验证码", Toast.LENGTH_SHORT).show() + return + } + if (verifyCode == null) { + Toast.makeText(this, "请先获取验证码", Toast.LENGTH_SHORT).show() + return + } + if (code != verifyCode) { + Toast.makeText(this, "验证码错误", Toast.LENGTH_SHORT).show() + return + } + + // 3. 密码校验 + if (TextUtils.isEmpty(newPassword) || TextUtils.isEmpty(confirmPassword)) { + Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show() + return + } + if (newPassword.length < MIN_PASSWORD_LENGTH || newPassword.length > MAX_PASSWORD_LENGTH) { + Toast.makeText(this, "密码长度需为${MIN_PASSWORD_LENGTH}-${MAX_PASSWORD_LENGTH}位", Toast.LENGTH_SHORT).show() + return + } + if (!newPassword.matches(Regex(".*[A-Za-z].*")) || !newPassword.matches(Regex(".*[0-9].*"))) { + Toast.makeText(this, "密码需同时包含字母和数字", Toast.LENGTH_SHORT).show() + return + } + if (newPassword != confirmPassword) { + Toast.makeText(this, "两次输入的密码不一致", Toast.LENGTH_SHORT).show() + return + } + + // 禁用确定按钮,防重复提交 + binding.btnNext.isEnabled = false + binding.btnNext.text = "重置中..." + + val requestJson = """ + { + "phone":"$phoneNumber", + "code":"$code", + "newPassword":"$newPassword" + } + """.trimIndent() + + OkHttpClientManager.postJson( + url = "https://httpbin.org/post", + json = requestJson + ) { result -> + result.onSuccess { + Toast.makeText(this, "密码重置成功,请重新登录", Toast.LENGTH_LONG).show() + finish() + }.onFailure { + Toast.makeText(this, "网络异常,请重试", Toast.LENGTH_SHORT).show() + } + + binding.btnNext.isEnabled = true + binding.btnNext.text = "确定" + } + } + + override fun onDestroy() { + super.onDestroy() + countDownTimer?.cancel() + countDownTimer = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/login/LoginActivity.kt b/app/src/main/java/com/example/defenseapplication/login/LoginActivity.kt new file mode 100644 index 0000000..ba42686 --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/login/LoginActivity.kt @@ -0,0 +1,162 @@ +package com.example.defenseapplication.login +import android.content.Intent +import com.example.defenseapplication.R +import android.os.Bundle +import android.os.CountDownTimer +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import com.example.defenseapplication.databinding.ActivityLoginBinding + +class LoginActivity : AppCompatActivity() { + + private lateinit var binding: ActivityLoginBinding + private var currentLoginMode = LoginMode.SMS + + enum class LoginMode { SMS, PASSWORD } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityLoginBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupTabListeners() + setupClickListeners() + switchToSmsMode() + } + + private fun setupTabListeners() { + binding.tvSmsLogin.setOnClickListener { + if (currentLoginMode != LoginMode.SMS) { + currentLoginMode = LoginMode.SMS + switchToSmsMode() + } + } + binding.tvPwdLogin.setOnClickListener { + if (currentLoginMode != LoginMode.PASSWORD) { + currentLoginMode = LoginMode.PASSWORD + switchToPasswordMode() + } + } + } + + private fun switchToSmsMode() { + binding.tvSmsLogin.setTextColor(getColor(R.color.black)) + binding.tvPwdLogin.setTextColor(getColor(R.color.gray)) + + binding.smsRowLayout.visibility = View.VISIBLE + binding.passwordRowLayout.visibility = View.GONE // 隐藏密码行容器 + + binding.registerText.visibility = View.VISIBLE + binding.forgetText.visibility = View.GONE + updateRegisterTextConstraints(center = true) + + //需要正确的方法名和参数 + updateLoginButtonTopConstraint(R.id.smsRowLayout) + } + + private fun switchToPasswordMode() { + binding.tvPwdLogin.setTextColor(getColor(R.color.black)) + binding.tvSmsLogin.setTextColor(getColor(R.color.gray)) + + binding.smsRowLayout.visibility = View.GONE + //正确的属性访问 + binding.passwordRowLayout.visibility = View.VISIBLE // 显示密码行容器 + + binding.registerText.visibility = View.VISIBLE + binding.forgetText.visibility = View.VISIBLE + updateRegisterTextConstraints(center = false) + + updateLoginButtonTopConstraint(R.id.passwordRowLayout) + } + + // 动态修改登录按钮的顶部约束 + private fun updateLoginButtonTopConstraint(topTargetId: Int) { + val params = binding.loginBtn.layoutParams as ConstraintLayout.LayoutParams + params.topToBottom = topTargetId + params.topMargin = 32 + binding.loginBtn.layoutParams = params + } + + // 动态修改立即注册的约束(居中/左对齐) + private fun updateRegisterTextConstraints(center: Boolean) { + val params = binding.registerText.layoutParams as ConstraintLayout.LayoutParams + if (center) { + // 短信模式:居中显示 + params.startToStart = ConstraintSet.PARENT_ID + params.endToEnd = ConstraintSet.PARENT_ID + } else { + // 密码模式:左对齐登录按钮 + params.startToStart = binding.loginBtn.id + params.endToEnd = ConstraintSet.UNSET + } + params.topToBottom = binding.loginBtn.id + params.topMargin = 20 + binding.registerText.layoutParams = params + } + + private fun setupClickListeners() { + binding.loginBtn.setOnClickListener { + val phone = binding.etPhone.text.toString() + if (phone.isEmpty()) { + Toast.makeText(this, "请输入手机号", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + when (currentLoginMode) { + LoginMode.SMS -> { + val code = binding.etSmsCode.text.toString() + if (code.isEmpty()) { + Toast.makeText(this, "请输入验证码", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(this, "验证码登录成功\n手机号:$phone\n验证码:$code", Toast.LENGTH_SHORT).show() + } + } + LoginMode.PASSWORD -> { + val pwd = binding.etPassword.text.toString() + if (pwd.isEmpty()) { + Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show() + } else { + Toast.makeText(this, "密码登录成功\n手机号:$phone\n密码:$pwd", Toast.LENGTH_SHORT).show() + } + } + } + } + + binding.sendCodeBtn.setOnClickListener { + val phone = binding.etPhone.text.toString() + if (phone.isEmpty()) { + Toast.makeText(this, "请先输入手机号", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + Toast.makeText(this, "验证码已发送至 $phone", Toast.LENGTH_SHORT).show() + startCountDown() + } + + binding.registerText.setOnClickListener { + val intent= Intent(this, RegisterActivity::class.java) + startActivity(intent) + Toast.makeText(this, "跳转到注册页面", Toast.LENGTH_SHORT).show() + } + + binding.forgetText.setOnClickListener { + val intent= Intent(this, ForgetPasswordActivity::class.java) + startActivity(intent) + Toast.makeText(this, "找回密码", Toast.LENGTH_SHORT).show() + } + } + + private fun startCountDown() { + binding.sendCodeBtn.isEnabled = false + object : CountDownTimer(60000, 1000) { + override fun onTick(millisUntilFinished: Long) { + binding.sendCodeBtn.text = "${millisUntilFinished / 1000}秒后重试" + } + override fun onFinish() { + binding.sendCodeBtn.text = "发送验证码" + binding.sendCodeBtn.isEnabled = true + } + }.start() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/login/RegisterActivity.kt b/app/src/main/java/com/example/defenseapplication/login/RegisterActivity.kt new file mode 100644 index 0000000..5556164 --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/login/RegisterActivity.kt @@ -0,0 +1,185 @@ +package com.example.defenseapplication.login + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.example.defenseapplication.databinding.RegisterActivityBinding + +/** + * 注册页面 + * 功能:收集用户信息(手机号、身份证号、密码、邀请码), + * 进行前端格式校验,模拟注册请求并反馈结果。 + */ +class RegisterActivity : AppCompatActivity() { + private lateinit var binding: RegisterActivityBinding + private var isLoading = false // 防止重复点击 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = RegisterActivityBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupListeners() + //setupTextWatchers() + } + + private fun setupListeners() { + // 返回按钮 + binding.backIcon.setOnClickListener { + finish() + } + + // 下一步按钮 + binding.btnNext.setOnClickListener { + if (!isLoading) { + //performRegistration() + } + } + } +// +// /** +// * 为输入框添加文字监听,实时清除错误提示 +// */ +// private fun setupTextWatchers() { +// val errorClearWatcher = object : TextWatcher { +// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} +// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} +// override fun afterTextChanged(s: Editable?) { +// // 当用户开始输入时,清除该字段的错误 +// when (binding.etPhoneNumber.hasFocus()) { +// true -> binding.etPhoneNumber.error = null +// else -> Unit +// } +// when (binding.etIdNumber.hasFocus()) { +// true -> binding.etIdNumber.error = null +// else -> Unit +// } +// when (binding.etPassword.hasFocus()) { +// true -> binding.etPassword.error = null +// else -> Unit +// } +// } +// } +// +// binding.etPhoneNumber.addTextChangedListener(errorClearWatcher) +// binding.etIdNumber.addTextChangedListener(errorClearWatcher) +// binding.etPassword.addTextChangedListener(errorClearWatcher) +// } +// +// /** +// * 执行注册流程:校验输入 -> 模拟网络请求 +// */ +// private fun performRegistration() { +// val phone = binding.etPhoneNumber.text.toString().trim() +// val idNumber = binding.etIdNumber.text.toString().trim() +// val password = binding.etPassword.text.toString().trim() +// val inviteCode = binding.etInviteCode.text.toString().trim() +// +// // 1. 校验 +// val phoneValid = validatePhone(phone) +// val idValid = validateIdNumber(idNumber) +// val passwordValid = validatePassword(password) +// +// if (!phoneValid) { +// binding.etPhoneNumber.error = "请输入有效的11位手机号" +// binding.etPhoneNumber.requestFocus() +// return +// } +// if (!idValid) { +// binding.etIdNumber.error = "请输入正确的身份证号" +// binding.etIdNumber.requestFocus() +// return +// } +// if (!passwordValid) { +// binding.etPassword.error = "密码长度需为6~20位" +// binding.etPassword.requestFocus() +// return +// } +// +// // 2. 模拟注册请求(展示加载状态) +// isLoading = true +// binding.btnNext.isEnabled = false +// binding.btnNext.text = "注册中..." +// +// lifecycleScope.launch { +// // 模拟网络延迟 1.5 秒 +// val registerResult = simulateRegister(phone, idNumber, password, inviteCode) +// delay(1500) +// +// isLoading = false +// binding.btnNext.isEnabled = true +// binding.btnNext.text = "下一步" +// +// if (registerResult) { +// Toast.makeText(this@RegisterActivity, "注册成功!", Toast.LENGTH_SHORT).show() +// // 实际项目中通常跳转到登录页或主页,这里简单关闭当前页面 +// finish() +// } else { +// Toast.makeText(this@RegisterActivity, "注册失败,请稍后重试", Toast.LENGTH_SHORT).show() +// } +// } +// } +// +// /** +// * 模拟注册请求(实际应替换为 Retrofit/OkHttp 调用) +// */ +// private suspend fun simulateRegister( +// phone: String, +// idNumber: String, +// password: String, +// inviteCode: String +// ): Boolean { +// // 模拟网络请求,此处始终返回成功(实际根据后端响应决定) +// // 您可以在这里添加真实网络请求逻辑 +// return true +// } +// /** +// * 校验手机号:中国大陆手机号(简单校验1开头的11位数字,第二位3-9) +// */ +// private fun validatePhone(phone: String): Boolean { +// val phoneRegex = Pattern.compile("^1[3-9]\\d{9}$") +// return phone.isNotEmpty() && phoneRegex.matcher(phone).matches() +// } +// +// /** +// * 校验身份证号(支持15位和18位,18位时校验最后一位校验码) +// */ +// private fun validateIdNumber(idNumber: String): Boolean { +// if (idNumber.isEmpty()) return false +// // 长度校验 +// if (idNumber.length != 15 && idNumber.length != 18) return false +// +// // 15位全数字 +// if (idNumber.length == 15) { +// return idNumber.all { it.isDigit() } +// } +// +// // 18位:前17位为数字,最后一位为数字或大写X +// val regex = Regex("^\\d{17}[\\dXx]$") +// if (!regex.matches(idNumber)) return false +// +// // 校验最后一位校验码(可选,提升准确性) +// return verifyIdCardChecksum(idNumber) +// } +// +// /** +// * 身份证最后一位校验码校验(仅18位) +// */ +// private fun verifyIdCardChecksum(id18: String): Boolean { +// val weights = intArrayOf(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2) +// val checkCodes = charArrayOf('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2') +// var sum = 0 +// for (i in 0 until 17) { +// sum += (id18[i] - '0') * weights[i] +// } +// val mod = sum % 11 +// val expectedCheckCode = checkCodes[mod] +// return id18[17].uppercaseChar() == expectedCheckCode +// } +// +// /** +// * 校验密码:长度6~20位 +// */ +// private fun validatePassword(password: String): Boolean { +// return password.length in 6..20 +// } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/defenseapplication/okhttp/OkHttpClient.kt b/app/src/main/java/com/example/defenseapplication/okhttp/OkHttpClient.kt new file mode 100644 index 0000000..ec7ca48 --- /dev/null +++ b/app/src/main/java/com/example/defenseapplication/okhttp/OkHttpClient.kt @@ -0,0 +1,4 @@ +package com.example.defenseapplication.okhttp + +class OkHttpClient { +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/rounded_blue_bg.xml b/app/src/main/res/drawable/rounded_blue_bg.xml new file mode 100644 index 0000000..6dbb0ba --- /dev/null +++ b/app/src/main/res/drawable/rounded_blue_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/textinput_white_bg.xml b/app/src/main/res/drawable/textinput_white_bg.xml new file mode 100644 index 0000000..4d3b37e --- /dev/null +++ b/app/src/main/res/drawable/textinput_white_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..ae83cfa --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/forget_password_activity.xml b/app/src/main/res/layout/forget_password_activity.xml new file mode 100644 index 0000000..4ade768 --- /dev/null +++ b/app/src/main/res/layout/forget_password_activity.xml @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/register_activity.xml b/app/src/main/res/layout/register_activity.xml new file mode 100644 index 0000000..364cffb --- /dev/null +++ b/app/src/main/res/layout/register_activity.xml @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/back.png b/app/src/main/res/mipmap-hdpi/back.png new file mode 100644 index 0000000..65c0eaa Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/back.png differ diff --git a/app/src/main/res/mipmap-hdpi/bg_colors.png b/app/src/main/res/mipmap-hdpi/bg_colors.png new file mode 100644 index 0000000..ae958b9 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/bg_colors.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic.png b/app/src/main/res/mipmap-hdpi/ic.png new file mode 100644 index 0000000..a7ffca6 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-hdpi/id_card.png b/app/src/main/res/mipmap-hdpi/id_card.png new file mode 100644 index 0000000..cbfd3e5 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/id_card.png differ diff --git a/app/src/main/res/mipmap-hdpi/lg.png b/app/src/main/res/mipmap-hdpi/lg.png new file mode 100644 index 0000000..642be53 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/lg.png differ diff --git a/app/src/main/res/mipmap-hdpi/loge.png b/app/src/main/res/mipmap-hdpi/loge.png new file mode 100644 index 0000000..4717d51 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/loge.png differ diff --git a/app/src/main/res/mipmap-hdpi/loge1.png b/app/src/main/res/mipmap-hdpi/loge1.png new file mode 100644 index 0000000..b18e92c Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/loge1.png differ diff --git a/app/src/main/res/mipmap-hdpi/ma.png b/app/src/main/res/mipmap-hdpi/ma.png new file mode 100644 index 0000000..b53e975 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ma.png differ diff --git a/app/src/main/res/mipmap-hdpi/vc.png b/app/src/main/res/mipmap-hdpi/vc.png new file mode 100644 index 0000000..42128fe Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/vc.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..9031ccf --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..93a63ad --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FF000000 + #FFFFFFFF + #09C2BD + #999999 + #EDF4FF + #E34D59 + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..55b8f47 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + DefenseApplication + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..48be0b6 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ + + + + +