Update Android app login, home, and face verification flows.

Sync current local UI and activity changes to remote repository.

Made-with: Cursor
This commit is contained in:
2026-04-27 15:39:47 +08:00
parent 2e6aba459c
commit b47704b281
24 changed files with 876 additions and 204 deletions

View File

@@ -2,6 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature
android:name="android.hardware.camera"
android:required="true" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -12,13 +18,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.DefenseApplication">
<activity
android:name=".login.ForgetPasswordActivity"
android:exported="false" />
<activity
android:name=".login.RegisterActivity"
android:exported="false" />
<activity
android:name=".login.LoginActivity"
android:name=".home.HomeActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -26,6 +26,25 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".login.RegisterSuccessActivity"
android:exported="false" />
<activity
android:name=".login.FaceVerificationActivity"
android:exported="false" />
<activity
android:name=".login.FaceLoginActivity"
android:exported="false" />
<activity
android:name=".login.ForgetPasswordActivity"
android:exported="false" />
<activity
android:name=".login.RegisterActivity"
android:exported="false" />
<activity
android:name=".login.LoginActivity"
android:exported="false"/>
</application>
</manifest>

View File

@@ -5,17 +5,75 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.HomeActivityBinding
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
class HomeActivity : AppCompatActivity() {
// ViewBinding 核心对象
private lateinit var binding: HomeActivityBinding
// 定义两个 Fragment
private val homeMenuFragment = HomeMenuFragment()
private val personalFragment = PersonalFragment()
// 当前显示的 Fragment
private var currentFragment: Fragment = homeMenuFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.home_activity)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
// 初始化 ViewBinding
binding = HomeActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
// 默认显示首页
switchFragment(homeMenuFragment)
// 第一次加载,只 add 首页
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, homeMenuFragment)
.commit()
// ========== 首页点击 ==========
binding.tabHome.setOnClickListener {
switchFragment(homeMenuFragment)
// 切换图标 + 文字颜色
binding.ivHome.setImageResource(R.drawable.home_checked)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_selected))
binding.ivProfile.setImageResource(R.drawable.profile)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_normal))
}
// ========== 个人点击 ==========
binding.tabProfile.setOnClickListener {
switchFragment(personalFragment)
// 切换图标 + 文字颜色
binding.ivHome.setImageResource(R.drawable.home)
binding.tvHome.setTextColor(resources.getColor(R.color.ic_normal))
binding.ivProfile.setImageResource(R.drawable.profile_checked)
binding.tvProfile.setTextColor(resources.getColor(R.color.ic_selected))
}
}
/**
* 切换 Fragment 的方法
*/
private fun switchFragment(fragment: Fragment) {
if (currentFragment == fragment) return
val transaction = supportFragmentManager.beginTransaction()
// 如果没添加过,才 add
if (!fragment.isAdded) {
transaction.add(R.id.fragment_container, fragment)
}
// 隐藏当前,显示目标
transaction.hide(currentFragment)
transaction.show(fragment)
transaction.commit()
currentFragment = fragment
}
}

View File

@@ -6,55 +6,24 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [HomeMenuFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class HomeMenuFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
// ViewBinding
private var _binding: HomeMenuFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.home_menu_fragment, container, false)
): View {
_binding = HomeMenuFragmentBinding.inflate(inflater, container, false)
return binding.root
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment HomeMenuFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
HomeMenuFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // 避免内存泄漏
}
}

View File

@@ -6,55 +6,24 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.PersonalFragmentBinding
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_PARAM1 = "param1"
private const val ARG_PARAM2 = "param2"
/**
* A simple [Fragment] subclass.
* Use the [PersonalFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class PersonalFragment : Fragment() {
// TODO: Rename and change types of parameters
private var param1: String? = null
private var param2: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
param2 = it.getString(ARG_PARAM2)
}
}
// ViewBinding
private var _binding: PersonalFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.personal_fragment, container, false)
): View {
_binding = PersonalFragmentBinding.inflate(inflater, container, false)
return binding.root
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment PersonalFragment.
*/
// TODO: Rename and change types and number of parameters
@JvmStatic
fun newInstance(param1: String, param2: String) =
PersonalFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
putString(ARG_PARAM2, param2)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null // 避免内存泄漏
}
}

View File

@@ -1,21 +1,39 @@
package com.example.defenseapplication.login
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.FaceLoginActivityBinding
class FaceLoginActivity : AppCompatActivity() {
private lateinit var binding: FaceLoginActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.face_login_activity)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
binding = FaceLoginActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
setupListeners()
}
private fun setupListeners() {
// 返回按钮
binding.backIcon.setOnClickListener {
finish()
}
// 继续按钮
binding.btnStartVerification.setOnClickListener {
val intent= Intent(this, FaceVerificationActivity::class.java)
startActivity(intent)
Toast.makeText(this, "跳转到人脸识别页面", Toast.LENGTH_SHORT).show()
}
}
}

View File

@@ -1,21 +1,110 @@
package com.example.defenseapplication.login
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.defenseapplication.databinding.FaceVerificationActivityBinding
import com.example.defenseapplication.view.FaceAnalyzer
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class FaceVerificationActivity : AppCompatActivity() {
private lateinit var binding: FaceVerificationActivityBinding
private lateinit var cameraExecutor: ExecutorService
private var faceDetector: com.google.mlkit.vision.face.FaceDetector? = null
companion object {
private const val REQUEST_CAMERA_PERMISSION = 100
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.face_verification_activity)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
// 使用标准 ViewBinding
binding = FaceVerificationActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
// 初始化人脸检测器(选项可自行调整)
val options = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_NONE)
.build()
faceDetector = FaceDetection.getClient(options)
cameraExecutor = Executors.newSingleThreadExecutor()
// 检查并请求权限
checkCameraPermissionAndStart()
}
private fun checkCameraPermissionAndStart() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
startCamera()
} else {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.CAMERA),
REQUEST_CAMERA_PERMISSION
)
}
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 预览用例
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(binding.previewView.surfaceProvider)
}
// 分析用例(人脸检测)
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(cameraExecutor, FaceAnalyzer(faceDetector, binding.overlay))
// 选择后置摄像头
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
// 绑定到生命周期
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis)
}, ContextCompat.getMainExecutor(this))
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startCamera()
} else {
Toast.makeText(this, "需要相机权限才能进行人脸验证", Toast.LENGTH_SHORT).show()
finish()
}
}
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
faceDetector?.close()
}
}

View File

@@ -12,7 +12,6 @@ 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
@@ -79,20 +78,6 @@ class ForgetPasswordActivity : AppCompatActivity() {
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
}
}
/**
@@ -181,20 +166,7 @@ class ForgetPasswordActivity : AppCompatActivity() {
}
""".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() {

View File

@@ -1,6 +1,8 @@
package com.example.defenseapplication.login
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.example.defenseapplication.databinding.RegisterActivityBinding
@@ -31,6 +33,9 @@ class RegisterActivity : AppCompatActivity() {
// 下一步按钮
binding.btnNext.setOnClickListener {
if (!isLoading) {
val intent= Intent(this, FaceLoginActivity::class.java)
startActivity(intent)
Toast.makeText(this, "跳转到人脸页面", Toast.LENGTH_SHORT).show()
//performRegistration()
}
}

View File

@@ -1,21 +1,63 @@
package com.example.defenseapplication.login
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.example.defenseapplication.R
import com.example.defenseapplication.databinding.RegisterSuccessActivityBinding
import com.example.defenseapplication.home.HomeActivity
class RegisterSuccessActivity : AppCompatActivity() {
private lateinit var binding: RegisterSuccessActivityBinding
private var countDownTimer: CountDownTimer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.register_success_activity)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
binding = RegisterSuccessActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
// 返回按钮
binding.backIcon.setOnClickListener {
finish()
}
// 点击按钮:取消倒计时并立即跳转
binding.btnEnter.setOnClickListener {
cancelTimer()
jumpToPlatform()
}
// 启动3秒倒计时自动跳转
startAutoJumpTimer()
}
private fun startAutoJumpTimer() {
countDownTimer = object : CountDownTimer(3000L, 1000L) {
override fun onTick(millisUntilFinished: Long) {
val seconds = (millisUntilFinished / 1000).toInt()
binding.btnEnter.text = "进入平台(${seconds}S)"
}
override fun onFinish() {
binding.btnEnter.text = "进入平台(0S)"
jumpToPlatform()
}
}.start()
}
private fun cancelTimer() {
countDownTimer?.cancel()
countDownTimer = null
}
private fun jumpToPlatform() {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
override fun onDestroy() {
super.onDestroy()
cancelTimer()
}
}

View File

@@ -1,4 +1,44 @@
package com.example.defenseapplication.view
class FaceAnalyzer {
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetector
/*
* 人脸分析器
* */
@ExperimentalGetImage
class FaceAnalyzer(
private val detector: FaceDetector?,
private val overlayView: FaceOverlayView
) : ImageAnalysis.Analyzer {
override fun analyze(imageProxy: ImageProxy) {
if (detector == null) {
imageProxy.close()
return
}
val mediaImage = imageProxy.image ?: run {
imageProxy.close()
return
}
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
detector.process(image)
.addOnSuccessListener { faces ->
Handler(Looper.getMainLooper()).post {
overlayView.setFaces(faces)
}
}
.addOnFailureListener { e ->
Log.e("FaceAnalyzer", "人脸检测失败", e)
}
.addOnCompleteListener {
imageProxy.close()
}
}
}

View File

@@ -1,2 +1,36 @@
package com.example.defenseapplication.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import com.google.mlkit.vision.face.Face
class FaceOverlayView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private val paint = Paint().apply {
color = Color.GREEN
style = Paint.Style.STROKE
strokeWidth = 4f
}
private val textPaint = Paint().apply {
color = Color.YELLOW
textSize = 40f
}
private var faces = mutableListOf<Face>()
fun setFaces(faces: List<Face>) {
this.faces = faces.toMutableList()
invalidate() // 触发 onDraw
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
for (face in faces) {
val rect = face.boundingBox
canvas.drawRect(rect, paint)
canvas.drawText("Face", rect.left.toFloat(), rect.top - 10f, textPaint)
}
}
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/green"/>
<corners android:radius="16dp"/>
<solid android:color="@color/face"/>
<corners android:radius="28dp"/>
</shape>

View File

@@ -25,7 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="智能防御机"
android:textSize="28sp"
android:textSize="26dp"
android:textStyle="bold"
android:textColor="@color/black"
app:layout_constraintTop_toBottomOf="@id/logoFrame"
@@ -86,8 +86,8 @@
android:paddingEnd="16dp">
<ImageView
android:id="@+id/ivPhoneIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/lg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -129,8 +129,8 @@
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivSmsIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/vc"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -188,8 +188,8 @@
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivPasswordIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ma"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"

View File

@@ -2,9 +2,293 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/login_background"
android:id="@+id/main"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".login.FaceLoginActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<ImageView
android:id="@+id/backIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- 标题:注册 -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="人脸识别"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/backIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/backIcon" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 副标题:注册用户 -->
<TextView
android:id="@+id/tvRegisterSubtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="人脸验证"
android:textColor="@color/black"
android:textSize="20dp"
android:textStyle="bold"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topContainer" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topSubContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvRegisterSubtitle"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<ImageView
android:id="@+id/safeIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/safe"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- 标题:注册 -->
<TextView
android:id="@+id/tvSubTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="仅用于创建防御机制和防窃取,保障您的安全"
android:textColor="@color/green"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/safeIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/safeIcon" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 人脸图标占位符(模拟人脸识别形象) -->
<ImageView
android:paddingTop="50dp"
android:id="@+id/cardFaceIcon"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="@drawable/face_verification"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topSubContainer" />
<!-- 提示信息 -->
<TextView
android:id="@+id/tvInstruction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="11dp"
android:text="请根据指示完成人脸验证"
android:textColor="@color/icon_face"
android:textSize="12dp"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cardFaceIcon" />
<LinearLayout
android:orientation="horizontal"
android:id="@+id/cbLowLight"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvInstruction"
android:paddingStart="16dp"
android:paddingTop="48dp"
android:paddingEnd="16dp">
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<LinearLayout
android:background="@drawable/face_login_gray_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="59dp"
android:layout_height="59dp"
android:src="@drawable/mask_group"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/common_icons_miniprogram2"/>
<TextView
android:layout_marginLeft="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标准拍摄"
android:textColor="@color/icon_face"
android:textSize="12dp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<LinearLayout
android:background="@drawable/face_login_gray_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="59dp"
android:layout_height="59dp"
android:src="@drawable/standard_capture"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/common_icons_miniprogram1"/>
<TextView
android:layout_marginLeft="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="标准拍摄"
android:textColor="@color/icon_face"
android:textSize="12dp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<LinearLayout
android:background="@drawable/face_login_gray_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="59dp"
android:layout_height="59dp"
android:src="@drawable/cropped_shot"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/common_icons_miniprogram1"/>
<TextView
android:layout_marginLeft="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍摄不全"
android:textColor="@color/icon_face"
android:textSize="12dp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<LinearLayout
android:background="@drawable/face_login_gray_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="59dp"
android:layout_height="59dp"
android:src="@drawable/low_light"/>
</LinearLayout>
<LinearLayout
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/common_icons_miniprogram1"/>
<TextView
android:layout_marginLeft="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="光线不足"
android:textColor="@color/icon_face"
android:textSize="12dp"
android:textStyle="bold"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!-- 开始验证按钮 -->
<android.widget.Button
android:id="@+id/btnStartVerification"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginTop="40dp"
android:layout_marginBottom="16dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:text="开始验证"
android:textColor="@color/white"
android:textSize="14dp"
android:textStyle="bold"
android:background="@drawable/rounded_blue_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cbLowLight" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -7,4 +7,14 @@
android:layout_height="match_parent"
tools:context=".login.FaceVerificationActivity">
<androidx.camera.view.PreviewView
android:id="@+id/previewView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.example.defenseapplication.view.FaceOverlayView
android:id="@+id/overlay"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -20,8 +20,8 @@
android:paddingEnd="16dp">
<ImageView
android:id="@+id/backIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -71,8 +71,8 @@
<ImageView
android:id="@+id/ivPhoneIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/lg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -115,8 +115,8 @@
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivSmsIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/vc"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -173,8 +173,8 @@
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivPasswordIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
@@ -214,8 +214,8 @@
<ImageView
android:id="@+id/ivInviteIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@@ -7,4 +7,73 @@
android:layout_height="match_parent"
tools:context=".home.HomeActivity">
<!-- Fragment 容器 -->
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/bottom_layout"
app:layout_constraintTop_toTopOf="parent"/>
<!-- 自定义底部导航LinearLayout-->
<LinearLayout
android:id="@+id/bottom_layout"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="#F9F9F9"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<!-- 首页 -->
<LinearLayout
android:id="@+id/tab_home"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:clickable="true">
<ImageView
android:id="@+id/iv_home"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/home_checked"/>
<TextView
android:id="@+id/tv_home"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="12sp"
android:textColor="@color/black"/>
</LinearLayout>
<!-- 个人 -->
<LinearLayout
android:id="@+id/tab_profile"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:clickable="true">
<ImageView
android:id="@+id/iv_profile"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/profile"/>
<TextView
android:id="@+id/tv_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="个人"
android:textSize="12sp"
android:textColor="@color/gray"/>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".home.HomeMenuFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页 Fragment"
android:textSize="20sp"/>
</LinearLayout>

View File

@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".home.PersonalFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="个人 Fragment"
android:textSize="20sp"/>
</FrameLayout>
</LinearLayout>

View File

@@ -20,8 +20,8 @@
android:paddingEnd="16dp">
<ImageView
android:id="@+id/backIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -33,12 +33,13 @@
android:layout_height="wrap_content"
android:text="注册"
android:textColor="@color/black"
android:textSize="32sp"
android:textSize="16dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/backIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/backIcon" />
app:layout_constraintTop_toTopOf="@id/backIcon"
app:layout_constraintVertical_bias="0.518" />
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -49,7 +50,7 @@
android:layout_height="wrap_content"
android:text="注册用户"
android:textColor="@color/black"
android:textSize="20dp"
android:textSize="24dp"
android:textStyle="bold"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
@@ -71,8 +72,8 @@
<ImageView
android:id="@+id/ivPhoneIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/lg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -113,8 +114,8 @@
<ImageView
android:id="@+id/ivIdIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/id_card"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -156,8 +157,8 @@
<!-- 左侧图标 -->
<ImageView
android:id="@+id/ivPasswordIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ma"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -198,8 +199,8 @@
<ImageView
android:id="@+id/ivInviteIcon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/ic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"

View File

@@ -1,10 +1,91 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".login.RegisterSuccessActivity">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/login_background"
android:id="@+id/main"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".login.RegisterSuccessActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<ImageView
android:id="@+id/backIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@mipmap/back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- 标题:注册 -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="注册"
android:textColor="@color/black"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/backIcon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/backIcon" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 副标题:注册用户 -->
<TextView
android:id="@+id/tvRegisterSubtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="注册用户"
android:textColor="@color/black"
android:textSize="20dp"
android:textStyle="bold"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/topContainer" />
<!--图标 -->
<ImageView
android:layout_marginTop="175dp"
android:id="@+id/cardFaceIcon"
android:layout_width="110dp"
android:layout_height="110dp"
android:src="@drawable/success"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvRegisterSubtitle" />
<!-- 进入平台3S -->
<android.widget.Button
android:id="@+id/btnEnter"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginTop="140dp"
android:layout_marginBottom="16dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
android:text="进入平台3S"
android:textColor="@color/white"
android:textSize="14dp"
android:textStyle="bold"
android:background="@drawable/rounded_blue_bg"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cardFaceIcon" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -6,5 +6,9 @@
<color name="gray">#999999</color>
<color name="login_background">#EDF4FF</color>
<color name="red">#E34D59</color>
<color name="icon_face">#666666</color>
<color name="face">#F0F0F0</color>
<color name="ic_selected">#000000 </color>
<color name="ic_normal">#999999 </color>
</resources>

View File

@@ -1,3 +1,5 @@
<resources>
<string name="app_name">DefenseApplication</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
</resources>