beautified UI
@@ -4,6 +4,7 @@ import android.Manifest
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Outline
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
@@ -14,9 +15,11 @@ import android.util.Size
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.ViewOutlineProvider
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -143,27 +146,116 @@ class MainActivity : AppCompatActivity() {
|
|||||||
cameraExecutor.shutdown()
|
cameraExecutor.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun dp(value: Int): Int {
|
||||||
|
return (value * resources.displayMetrics.density).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun primaryButton(text: String, onClick: () -> Unit): Button {
|
||||||
|
return Button(this).apply {
|
||||||
|
this.text = text
|
||||||
|
textSize = 16f
|
||||||
|
setTextColor(0xFFFFFFFF.toInt())
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
isAllCaps = false
|
||||||
|
setBackgroundResource(R.drawable.bg_button_primary)
|
||||||
|
minHeight = dp(48)
|
||||||
|
setPadding(dp(22), 0, dp(22), 0)
|
||||||
|
setOnClickListener { onClick() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun secondaryButton(text: String, onClick: () -> Unit): Button {
|
||||||
|
return Button(this).apply {
|
||||||
|
this.text = text
|
||||||
|
textSize = 16f
|
||||||
|
setTextColor(0xFF1D4ED8.toInt())
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
isAllCaps = false
|
||||||
|
setBackgroundResource(R.drawable.bg_button_secondary)
|
||||||
|
minHeight = dp(48)
|
||||||
|
setPadding(dp(22), 0, dp(22), 0)
|
||||||
|
setOnClickListener { onClick() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cardButton(iconRes: Int, title: String, subtitle: String, onClick: () -> Unit): View {
|
||||||
|
return LinearLayout(this).apply {
|
||||||
|
orientation = LinearLayout.HORIZONTAL
|
||||||
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
|
setBackgroundResource(R.drawable.bg_card_click)
|
||||||
|
isClickable = true
|
||||||
|
isFocusable = true
|
||||||
|
elevation = dp(3).toFloat()
|
||||||
|
setPadding(dp(20), dp(18), dp(20), dp(18))
|
||||||
|
setOnClickListener { onClick() }
|
||||||
|
|
||||||
|
addView(FrameLayout(this@MainActivity).apply {
|
||||||
|
layoutParams = LinearLayout.LayoutParams(dp(52), dp(52))
|
||||||
|
setBackgroundResource(R.drawable.bg_button_primary)
|
||||||
|
addView(ImageView(this@MainActivity).apply {
|
||||||
|
layoutParams = FrameLayout.LayoutParams(dp(28), dp(28), Gravity.CENTER)
|
||||||
|
setImageResource(iconRes)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
addView(LinearLayout(this@MainActivity).apply {
|
||||||
|
orientation = LinearLayout.VERTICAL
|
||||||
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
|
setPadding(dp(16), 0, 0, 0)
|
||||||
|
|
||||||
|
addView(TextView(this@MainActivity).apply {
|
||||||
|
text = title
|
||||||
|
textSize = 18f
|
||||||
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
setTextColor(0xFF0F172A.toInt())
|
||||||
|
})
|
||||||
|
addView(TextView(this@MainActivity).apply {
|
||||||
|
text = subtitle
|
||||||
|
textSize = 13f
|
||||||
|
setTextColor(0xFF64748B.toInt())
|
||||||
|
setPadding(0, dp(4), 0, 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
addView(TextView(this@MainActivity).apply {
|
||||||
|
text = "›"
|
||||||
|
textSize = 26f
|
||||||
|
setTextColor(0xFF94A3B8.toInt())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createContentView(): View {
|
private fun createContentView(): View {
|
||||||
val root = LinearLayout(this).apply {
|
val root = LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setBackgroundColor(0xFFF8FAFC.toInt())
|
setBackgroundColor(0xFFF1F5F9.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
val header = LinearLayout(this).apply {
|
val header = LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setPadding(32, 28, 32, 20)
|
setBackgroundResource(R.drawable.bg_header)
|
||||||
|
setPadding(dp(24), dp(58), dp(24), dp(18))
|
||||||
}
|
}
|
||||||
header.addView(TextView(this).apply {
|
header.addView(TextView(this).apply {
|
||||||
text = "学生人脸识别"
|
text = "学生人脸识别"
|
||||||
textSize = 24f
|
textSize = 22f
|
||||||
setTextColor(0xFF0F172A.toInt())
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
setTextColor(0xFFFFFFFF.toInt())
|
||||||
})
|
})
|
||||||
countText = TextView(this).apply {
|
countText = TextView(this).apply {
|
||||||
text = "已入库 0 名学生"
|
text = "已入库 0 名学生"
|
||||||
textSize = 15f
|
textSize = 13f
|
||||||
setTextColor(0xFF475569.toInt())
|
setTextColor(0xFFFFFFFF.toInt())
|
||||||
|
setBackgroundResource(R.drawable.bg_chip_white)
|
||||||
|
setPadding(dp(12), dp(6), dp(12), dp(6))
|
||||||
}
|
}
|
||||||
header.addView(countText)
|
header.addView(
|
||||||
|
countText,
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { topMargin = dp(10) }
|
||||||
|
)
|
||||||
|
|
||||||
val content = FrameLayout(this).apply {
|
val content = FrameLayout(this).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
@@ -192,66 +284,83 @@ class MainActivity : AppCompatActivity() {
|
|||||||
private fun createHomePanel(): View {
|
private fun createHomePanel(): View {
|
||||||
return LinearLayout(this).apply {
|
return LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER_HORIZONTAL
|
||||||
setPadding(40, 40, 40, 40)
|
setPadding(dp(24), dp(36), dp(24), dp(36))
|
||||||
|
|
||||||
addView(TextView(this@MainActivity).apply {
|
addView(TextView(this@MainActivity).apply {
|
||||||
text = "请选择要进入的功能"
|
text = "请选择要进入的功能"
|
||||||
textSize = 26f
|
textSize = 22f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
})
|
})
|
||||||
|
|
||||||
addView(TextView(this@MainActivity).apply {
|
addView(TextView(this@MainActivity).apply {
|
||||||
text = "识别和注册上传分开运行,减少同时占用相机、模型和本地服务带来的卡顿。"
|
text = "识别与注册上传分开运行,避免同时占用相机和模型导致卡顿。"
|
||||||
textSize = 16f
|
textSize = 14f
|
||||||
setTextColor(0xFF475569.toInt())
|
setTextColor(0xFF64748B.toInt())
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
setPadding(0, 20, 0, 36)
|
setPadding(0, dp(10), 0, dp(28))
|
||||||
})
|
})
|
||||||
|
|
||||||
addView(Button(this@MainActivity).apply {
|
addView(
|
||||||
text = "进入识别"
|
cardButton(
|
||||||
textSize = 20f
|
R.drawable.ic_face_scan,
|
||||||
setPadding(36, 24, 36, 24)
|
"进入识别",
|
||||||
setOnClickListener { switchMode(AppMode.RECOGNITION) }
|
"打开摄像头,实时识别学生身份"
|
||||||
})
|
) { switchMode(AppMode.RECOGNITION) },
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { bottomMargin = dp(16) }
|
||||||
|
)
|
||||||
|
|
||||||
addView(Button(this@MainActivity).apply {
|
addView(
|
||||||
text = "进入注册上传"
|
cardButton(
|
||||||
textSize = 20f
|
R.drawable.ic_upload,
|
||||||
setPadding(36, 24, 36, 24)
|
"进入注册上传",
|
||||||
setOnClickListener { switchMode(AppMode.UPLOAD) }
|
"上传学生照片或视频,更新人脸库"
|
||||||
})
|
) { switchMode(AppMode.UPLOAD) },
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRecognitionPanel(): View {
|
private fun createRecognitionPanel(): View {
|
||||||
return LinearLayout(this).apply {
|
return LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setPadding(24, 16, 24, 24)
|
setPadding(dp(16), dp(12), dp(16), dp(16))
|
||||||
|
|
||||||
recognitionStatusText = TextView(this@MainActivity).apply {
|
recognitionStatusText = TextView(this@MainActivity).apply {
|
||||||
text = "准备进入识别模式"
|
text = "准备进入识别模式"
|
||||||
textSize = 22f
|
textSize = 18f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
setPadding(8, 8, 8, 20)
|
setPadding(dp(8), dp(4), dp(8), dp(12))
|
||||||
}
|
}
|
||||||
addView(recognitionStatusText)
|
addView(recognitionStatusText)
|
||||||
|
|
||||||
val matcherConfigPanel = LinearLayout(this@MainActivity).apply {
|
val matcherConfigPanel = LinearLayout(this@MainActivity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setPadding(16, 16, 16, 16)
|
setPadding(dp(16), dp(14), dp(16), dp(14))
|
||||||
setBackgroundColor(0xFFF1F5F9.toInt())
|
setBackgroundResource(R.drawable.bg_panel_light)
|
||||||
}
|
}
|
||||||
|
|
||||||
matcherConfigSummaryText = TextView(this@MainActivity).apply {
|
matcherConfigPanel.addView(TextView(this@MainActivity).apply {
|
||||||
|
text = "识别参数"
|
||||||
textSize = 14f
|
textSize = 14f
|
||||||
setTextColor(0xFF475569.toInt())
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
|
setTextColor(0xFF0F172A.toInt())
|
||||||
|
})
|
||||||
|
|
||||||
|
matcherConfigSummaryText = TextView(this@MainActivity).apply {
|
||||||
|
textSize = 12f
|
||||||
|
setTextColor(0xFF64748B.toInt())
|
||||||
text = matcherConfigSummary()
|
text = matcherConfigSummary()
|
||||||
setPadding(0, 0, 0, 12)
|
setPadding(0, dp(2), 0, dp(10))
|
||||||
}
|
}
|
||||||
matcherConfigPanel.addView(matcherConfigSummaryText)
|
matcherConfigPanel.addView(matcherConfigSummaryText)
|
||||||
|
|
||||||
@@ -262,7 +371,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val euclideanColumn = LinearLayout(this@MainActivity).apply {
|
val euclideanColumn = LinearLayout(this@MainActivity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
setPadding(0, 0, 12, 0)
|
setPadding(0, 0, dp(8), 0)
|
||||||
}
|
}
|
||||||
euclideanColumn.addView(TextView(this@MainActivity).apply {
|
euclideanColumn.addView(TextView(this@MainActivity).apply {
|
||||||
text = "欧氏阈值"
|
text = "欧氏阈值"
|
||||||
@@ -273,15 +382,25 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setText(formatThreshold(euclideanThreshold))
|
setText(formatThreshold(euclideanThreshold))
|
||||||
hint = "0.00"
|
hint = "0.00"
|
||||||
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
textSize = 15f
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
setBackgroundColor(0xFFFFFFFF.toInt())
|
setBackgroundResource(R.drawable.bg_input)
|
||||||
setPadding(20, 18, 20, 18)
|
setPadding(dp(12), 0, dp(12), 0)
|
||||||
|
minHeight = dp(44)
|
||||||
}
|
}
|
||||||
euclideanColumn.addView(euclideanThresholdInput)
|
euclideanColumn.addView(
|
||||||
|
euclideanThresholdInput,
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { topMargin = dp(6) }
|
||||||
|
)
|
||||||
|
thresholdRow.addView(euclideanColumn)
|
||||||
|
|
||||||
val cosineColumn = LinearLayout(this@MainActivity).apply {
|
val cosineColumn = LinearLayout(this@MainActivity).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
|
setPadding(dp(8), 0, 0, 0)
|
||||||
}
|
}
|
||||||
cosineColumn.addView(TextView(this@MainActivity).apply {
|
cosineColumn.addView(TextView(this@MainActivity).apply {
|
||||||
text = "余弦阈值"
|
text = "余弦阈值"
|
||||||
@@ -291,26 +410,37 @@ class MainActivity : AppCompatActivity() {
|
|||||||
cosineThresholdInput = EditText(this@MainActivity).apply {
|
cosineThresholdInput = EditText(this@MainActivity).apply {
|
||||||
setText(formatThreshold(cosineThreshold))
|
setText(formatThreshold(cosineThreshold))
|
||||||
hint = "0.00"
|
hint = "0.00"
|
||||||
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
|
inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
textSize = 15f
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
setBackgroundColor(0xFFFFFFFF.toInt())
|
setBackgroundResource(R.drawable.bg_input)
|
||||||
setPadding(20, 18, 20, 18)
|
setPadding(dp(12), 0, dp(12), 0)
|
||||||
|
minHeight = dp(44)
|
||||||
}
|
}
|
||||||
cosineColumn.addView(cosineThresholdInput)
|
cosineColumn.addView(
|
||||||
|
cosineThresholdInput,
|
||||||
thresholdRow.addView(euclideanColumn)
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { topMargin = dp(6) }
|
||||||
|
)
|
||||||
thresholdRow.addView(cosineColumn)
|
thresholdRow.addView(cosineColumn)
|
||||||
matcherConfigPanel.addView(thresholdRow)
|
matcherConfigPanel.addView(thresholdRow)
|
||||||
|
|
||||||
matcherConfigPanel.addView(Button(this@MainActivity).apply {
|
matcherConfigPanel.addView(LinearLayout(this@MainActivity).apply {
|
||||||
text = "保存识别参数"
|
orientation = LinearLayout.HORIZONTAL
|
||||||
setOnClickListener { saveMatcherConfig() }
|
setPadding(0, dp(12), 0, 0)
|
||||||
})
|
|
||||||
matcherConfigPanel.addView(Button(this@MainActivity).apply {
|
|
||||||
text = "自动校准阈值"
|
|
||||||
setOnClickListener { applySuggestedThresholds() }
|
|
||||||
})
|
|
||||||
|
|
||||||
|
addView(
|
||||||
|
secondaryButton("自动校准阈值") { applySuggestedThresholds() },
|
||||||
|
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
|
.apply { rightMargin = dp(8) }
|
||||||
|
)
|
||||||
|
addView(
|
||||||
|
primaryButton("保存识别参数") { saveMatcherConfig() },
|
||||||
|
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
|
)
|
||||||
|
})
|
||||||
addView(matcherConfigPanel)
|
addView(matcherConfigPanel)
|
||||||
|
|
||||||
val cameraFrame = FrameLayout(this@MainActivity).apply {
|
val cameraFrame = FrameLayout(this@MainActivity).apply {
|
||||||
@@ -319,39 +449,51 @@ class MainActivity : AppCompatActivity() {
|
|||||||
0,
|
0,
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
|
setBackgroundResource(R.drawable.bg_camera_frame)
|
||||||
|
setPadding(dp(4), dp(4), dp(4), dp(4))
|
||||||
}
|
}
|
||||||
|
cameraFrame.addView(
|
||||||
|
FrameLayout(this@MainActivity).apply {
|
||||||
|
clipToOutline = true
|
||||||
|
outlineProvider = object : ViewOutlineProvider() {
|
||||||
|
override fun getOutline(view: View, outline: Outline) {
|
||||||
|
outline.setRoundRect(0, 0, view.width, view.height, dp(16).toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
previewView = PreviewView(this@MainActivity).apply {
|
previewView = PreviewView(this@MainActivity).apply {
|
||||||
scaleType = PreviewView.ScaleType.FILL_CENTER
|
scaleType = PreviewView.ScaleType.FILL_CENTER
|
||||||
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
|
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
|
||||||
}
|
}
|
||||||
overlayView = OverlayView(this@MainActivity)
|
overlayView = OverlayView(this@MainActivity)
|
||||||
cameraFrame.addView(previewView, FrameLayout.LayoutParams(-1, -1))
|
addView(previewView, FrameLayout.LayoutParams(-1, -1))
|
||||||
cameraFrame.addView(overlayView, FrameLayout.LayoutParams(-1, -1))
|
addView(overlayView, FrameLayout.LayoutParams(-1, -1))
|
||||||
|
},
|
||||||
|
FrameLayout.LayoutParams(-1, -1)
|
||||||
|
)
|
||||||
addView(cameraFrame)
|
addView(cameraFrame)
|
||||||
|
|
||||||
addView(LinearLayout(this@MainActivity).apply {
|
addView(LinearLayout(this@MainActivity).apply {
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
setPadding(12, 20, 12, 8)
|
setPadding(dp(12), dp(16), dp(12), 0)
|
||||||
|
|
||||||
addView(Button(this@MainActivity).apply {
|
addView(
|
||||||
text = "返回首页"
|
secondaryButton("返回首页") { switchMode(AppMode.HOME) },
|
||||||
setOnClickListener { switchMode(AppMode.HOME) }
|
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
})
|
.apply { rightMargin = dp(10) }
|
||||||
|
)
|
||||||
|
|
||||||
pauseRecognitionButton = Button(this@MainActivity).apply {
|
pauseRecognitionButton = primaryButton("暂停识别") {
|
||||||
text = "暂停识别"
|
|
||||||
setOnClickListener {
|
|
||||||
recognitionEnabled = !recognitionEnabled
|
recognitionEnabled = !recognitionEnabled
|
||||||
text = if (recognitionEnabled) "暂停识别" else "继续识别"
|
pauseRecognitionButton.text = if (recognitionEnabled) "暂停识别" else "继续识别"
|
||||||
recognitionStatusText.text = if (recognitionEnabled) "正在识别" else "识别已暂停"
|
recognitionStatusText.text = if (recognitionEnabled) "正在识别" else "识别已暂停"
|
||||||
if (!recognitionEnabled) {
|
if (!recognitionEnabled) {
|
||||||
overlayView.update(emptyList())
|
overlayView.update(emptyList())
|
||||||
}
|
}
|
||||||
updateAnalyzerState()
|
updateAnalyzerState()
|
||||||
}
|
}
|
||||||
}
|
addView(pauseRecognitionButton, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f))
|
||||||
addView(pauseRecognitionButton)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,11 +502,11 @@ class MainActivity : AppCompatActivity() {
|
|||||||
return LinearLayout(this).apply {
|
return LinearLayout(this).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
gravity = Gravity.CENTER_HORIZONTAL
|
gravity = Gravity.CENTER_HORIZONTAL
|
||||||
setPadding(40, 48, 40, 48)
|
setPadding(dp(24), dp(32), dp(24), dp(32))
|
||||||
|
|
||||||
uploadStatusText = TextView(this@MainActivity).apply {
|
uploadStatusText = TextView(this@MainActivity).apply {
|
||||||
text = "准备进入注册上传模式"
|
text = "准备进入注册上传模式"
|
||||||
textSize = 24f
|
textSize = 20f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
@@ -373,38 +515,62 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
addView(TextView(this@MainActivity).apply {
|
addView(TextView(this@MainActivity).apply {
|
||||||
setText(R.string.upload_instructions)
|
setText(R.string.upload_instructions)
|
||||||
textSize = 16f
|
textSize = 14f
|
||||||
setTextColor(0xFF475569.toInt())
|
setTextColor(0xFF64748B.toInt())
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
setPadding(0, 16, 0, 20)
|
setPadding(0, dp(10), 0, dp(24))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
addView(
|
||||||
|
LinearLayout(this@MainActivity).apply {
|
||||||
|
orientation = LinearLayout.VERTICAL
|
||||||
|
setBackgroundResource(R.drawable.bg_card)
|
||||||
|
elevation = dp(3).toFloat()
|
||||||
|
setPadding(dp(18), dp(16), dp(18), dp(16))
|
||||||
|
|
||||||
|
addView(TextView(this@MainActivity).apply {
|
||||||
|
text = "上传地址"
|
||||||
|
textSize = 12f
|
||||||
|
setTextColor(0xFF64748B.toInt())
|
||||||
|
})
|
||||||
uploadUrlText = TextView(this@MainActivity).apply {
|
uploadUrlText = TextView(this@MainActivity).apply {
|
||||||
text = "-"
|
text = "-"
|
||||||
textSize = 20f
|
textSize = 18f
|
||||||
typeface = Typeface.MONOSPACE
|
typeface = Typeface.MONOSPACE
|
||||||
setTextColor(0xFF0F172A.toInt())
|
setTextColor(0xFF0F172A.toInt())
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
setPadding(24, 20, 24, 20)
|
setPadding(dp(12), dp(12), dp(12), dp(12))
|
||||||
setTextIsSelectable(true)
|
setTextIsSelectable(true)
|
||||||
setBackgroundColor(0xFFE2E8F0.toInt())
|
setBackgroundResource(R.drawable.bg_input)
|
||||||
}
|
}
|
||||||
addView(uploadUrlText)
|
addView(
|
||||||
|
uploadUrlText,
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
).apply { topMargin = dp(8) }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
addView(LinearLayout(this@MainActivity).apply {
|
addView(LinearLayout(this@MainActivity).apply {
|
||||||
gravity = Gravity.CENTER
|
gravity = Gravity.CENTER
|
||||||
orientation = LinearLayout.HORIZONTAL
|
orientation = LinearLayout.HORIZONTAL
|
||||||
setPadding(12, 28, 12, 8)
|
setPadding(0, dp(24), 0, 0)
|
||||||
|
|
||||||
addView(Button(this@MainActivity).apply {
|
addView(
|
||||||
text = "刷新地址"
|
secondaryButton("刷新地址") { startUploadSession() },
|
||||||
setOnClickListener { startUploadSession() }
|
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
})
|
.apply { rightMargin = dp(10) }
|
||||||
|
)
|
||||||
addView(Button(this@MainActivity).apply {
|
addView(
|
||||||
text = "返回首页"
|
primaryButton("返回首页") { switchMode(AppMode.HOME) },
|
||||||
setOnClickListener { switchMode(AppMode.HOME) }
|
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
|
||||||
})
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,16 @@ class OverlayView @JvmOverloads constructor(
|
|||||||
attrs: AttributeSet? = null
|
attrs: AttributeSet? = null
|
||||||
) : View(context, attrs) {
|
) : View(context, attrs) {
|
||||||
|
|
||||||
|
// 识别框外发光画笔
|
||||||
|
private val glowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 14f
|
||||||
|
}
|
||||||
|
|
||||||
// 识别框画笔
|
// 识别框画笔
|
||||||
private val boxPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val boxPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
style = Paint.Style.STROKE
|
style = Paint.Style.STROKE
|
||||||
strokeWidth = 6f
|
strokeWidth = 5f
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标签背景画笔
|
// 标签背景画笔
|
||||||
@@ -31,22 +37,29 @@ class OverlayView @JvmOverloads constructor(
|
|||||||
style = Paint.Style.FILL
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 标签投影画笔
|
||||||
|
private val labelShadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
style = Paint.Style.FILL
|
||||||
|
color = Color.argb(70, 0, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
// 标签文字画笔
|
// 标签文字画笔
|
||||||
private val labelPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val labelPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = Color.WHITE
|
color = Color.WHITE
|
||||||
textSize = 48f
|
textSize = 40f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.create("sans-serif-medium", Typeface.BOLD)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 置信度文字画笔
|
// 置信度文字画笔
|
||||||
private val confidencePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val confidencePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = Color.WHITE
|
color = Color.WHITE
|
||||||
textSize = 32f
|
textSize = 30f
|
||||||
|
typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 颜色定义
|
// 颜色定义
|
||||||
private val colorMatch = Color.rgb(34, 197, 94) // 绿色 - 匹配成功
|
private val colorMatch = Color.rgb(34, 197, 94) // 绿色 - 匹配成功
|
||||||
private val colorUnsure = Color.rgb(234, 179, 8) // 黄色 - 不确定
|
private val colorUnsure = Color.rgb(245, 158, 11) // 橙色 - 不确定
|
||||||
private val colorNoMatch = Color.rgb(239, 68, 68) // 红色 - 未匹配
|
private val colorNoMatch = Color.rgb(239, 68, 68) // 红色 - 未匹配
|
||||||
private val colorUnknown = Color.rgb(59, 130, 246) // 蓝色 - 识别中
|
private val colorUnknown = Color.rgb(59, 130, 246) // 蓝色 - 识别中
|
||||||
|
|
||||||
@@ -72,43 +85,74 @@ class OverlayView @JvmOverloads constructor(
|
|||||||
val boxColor = getBoxColor(item.result)
|
val boxColor = getBoxColor(item.result)
|
||||||
boxPaint.color = boxColor
|
boxPaint.color = boxColor
|
||||||
|
|
||||||
// 绘制识别框
|
// 外发光(半透明描边)
|
||||||
canvas.drawRect(rect.left, rect.top, rect.right, rect.bottom, boxPaint)
|
glowPaint.color = boxColor and 0x00FFFFFF or 0x4D000000.toInt()
|
||||||
|
canvas.drawRoundRect(rect, 14f, 14f, glowPaint)
|
||||||
|
|
||||||
|
// 绘制识别框(圆角)
|
||||||
|
canvas.drawRoundRect(rect, 14f, 14f, boxPaint)
|
||||||
|
|
||||||
// 绘制标签背景
|
// 绘制标签背景
|
||||||
val label = item.label
|
val label = item.label
|
||||||
val labelWidth = labelPaint.measureText(label)
|
val labelWidth = labelPaint.measureText(label)
|
||||||
val labelHeight = 60f
|
val labelHeight = 52f
|
||||||
val padding = 12f
|
val padding = 14f
|
||||||
|
|
||||||
// 标签在框上方
|
// 标签在框上方
|
||||||
val labelLeft = rect.left + padding
|
val labelLeft = rect.left + 4f
|
||||||
val labelTop = (rect.top - labelHeight - padding).coerceAtLeast(padding)
|
val labelTop = (rect.top - labelHeight - 10f).coerceAtLeast(padding)
|
||||||
val labelRight = labelLeft + labelWidth + padding * 2
|
val labelRight = labelLeft + labelWidth + padding * 2
|
||||||
val labelBottom = labelTop + labelHeight
|
val labelBottom = labelTop + labelHeight
|
||||||
|
|
||||||
// 标签背景颜色与框颜色一致
|
// 标签投影
|
||||||
|
canvas.drawRoundRect(
|
||||||
|
labelLeft,
|
||||||
|
labelTop + 3f,
|
||||||
|
labelRight,
|
||||||
|
labelBottom + 3f,
|
||||||
|
labelHeight / 2f,
|
||||||
|
labelHeight / 2f,
|
||||||
|
labelShadowPaint
|
||||||
|
)
|
||||||
|
|
||||||
|
// 标签背景颜色与框颜色一致(胶囊形)
|
||||||
labelBackgroundPaint.color = boxColor
|
labelBackgroundPaint.color = boxColor
|
||||||
canvas.drawRoundRect(labelLeft, labelTop, labelRight, labelBottom, 8f, 8f, labelBackgroundPaint)
|
canvas.drawRoundRect(
|
||||||
|
labelLeft,
|
||||||
|
labelTop,
|
||||||
|
labelRight,
|
||||||
|
labelBottom,
|
||||||
|
labelHeight / 2f,
|
||||||
|
labelHeight / 2f,
|
||||||
|
labelBackgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
// 绘制标签文字
|
// 绘制标签文字
|
||||||
canvas.drawText(label, labelLeft + padding, labelTop + 38f, labelPaint)
|
canvas.drawText(label, labelLeft + padding, labelTop + 36f, labelPaint)
|
||||||
|
|
||||||
// 如果有置信度信息,显示在标签右侧
|
// 如果有置信度信息,显示在标签右侧
|
||||||
if (item.confidence != null) {
|
if (item.confidence != null) {
|
||||||
val confidenceText = "${(item.confidence * 100).toInt()}%"
|
val confidenceText = "${(item.confidence * 100).toInt()}%"
|
||||||
val confidenceWidth = confidencePaint.measureText(confidenceText)
|
val confidenceWidth = confidencePaint.measureText(confidenceText)
|
||||||
val confLeft = labelRight + 8f
|
val chipLeft = labelRight + 8f
|
||||||
val confTop = labelTop + 8f
|
val chipTop = labelTop + 6f
|
||||||
val confRight = confLeft + confidenceWidth + 16f
|
val chipRight = chipLeft + confidenceWidth + 22f
|
||||||
val confBottom = confTop + 44f
|
val chipBottom = chipTop + 40f
|
||||||
|
|
||||||
// 置信度背景(半透明黑色)
|
// 置信度背景(深色半透明胶囊)
|
||||||
labelBackgroundPaint.color = Color.argb(180, 0, 0, 0)
|
labelBackgroundPaint.color = Color.argb(210, 15, 23, 42)
|
||||||
canvas.drawRoundRect(confLeft, confTop, confRight, confBottom, 6f, 6f, labelBackgroundPaint)
|
canvas.drawRoundRect(
|
||||||
|
chipLeft,
|
||||||
|
chipTop,
|
||||||
|
chipRight,
|
||||||
|
chipBottom,
|
||||||
|
20f,
|
||||||
|
20f,
|
||||||
|
labelBackgroundPaint
|
||||||
|
)
|
||||||
|
|
||||||
// 置信度文字
|
// 置信度文字
|
||||||
canvas.drawText(confidenceText, confLeft + 8f, confTop + 30f, confidencePaint)
|
canvas.drawText(confidenceText, chipLeft + 11f, chipTop + 27f, confidencePaint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
app/src/main/res/drawable/bg_button_primary.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#33FFFFFF">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:startColor="#2E6BFF"
|
||||||
|
android:endColor="#1D4ED8" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
13
app/src/main/res/drawable/bg_button_secondary.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#1A2563EB">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#CBD5E1" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
8
app/src/main/res/drawable/bg_camera_frame.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#E2E8F0" />
|
||||||
|
</shape>
|
||||||
8
app/src/main/res/drawable/bg_card.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#E2E8F0" />
|
||||||
|
</shape>
|
||||||
13
app/src/main/res/drawable/bg_card_click.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="#1A2563EB">
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="20dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#E2E8F0" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
5
app/src/main/res/drawable/bg_chip_white.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#26FFFFFF" />
|
||||||
|
<corners android:radius="999dp" />
|
||||||
|
</shape>
|
||||||
7
app/src/main/res/drawable/bg_header.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:startColor="#2E6BFF"
|
||||||
|
android:endColor="#1D4ED8" />
|
||||||
|
</shape>
|
||||||
8
app/src/main/res/drawable/bg_input.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#FFFFFF" />
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#CBD5E1" />
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/bg_panel_light.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#F1F5F9" />
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
</shape>
|
||||||
47
app/src/main/res/drawable/ic_face_scan.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,6.4 A4.2,4.2 0 1 1 12,14.8 A4.2,4.2 0 1 1 12,6.4 Z"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="1.7"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M6.2,20.2 A5.8,5.8 0 0 1 17.8,20.2"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="1.7"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M4,8.2 L4,5.2 C4,4.55 4.55,4 5.2,4 L8.2,4"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M15.8,4 L18.8,4 C19.45,4 20,4.55 20,5.2 L20,8.2"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M4,15.8 L4,18.8 C4,19.45 4.55,20 5.2,20 L8.2,20"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M15.8,20 L18.8,20 C19.45,20 20,19.45 20,18.8 L20,15.8"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
</vector>
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="#1D4ED8" />
|
<gradient
|
||||||
|
android:angle="315"
|
||||||
|
android:startColor="#3B82F6"
|
||||||
|
android:centerColor="#2563EB"
|
||||||
|
android:endColor="#1E40AF" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
47
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:pathData="M54,32 A15,15 0 1 1 54,62 A15,15 0 1 1 54,32 Z"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M36,84 A18,18 0 0 1 72,84"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M25,40 L25,33 C25,30.6 26.6,29 29,29 L36,29"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M72,29 L79,29 C81.4,29 83,30.6 83,33 L83,40"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M25,68 L25,75 C25,77.4 26.6,79 29,79 L36,79"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M72,79 L79,79 C81.4,79 83,77.4 83,75 L83,68"
|
||||||
|
android:strokeColor="#7DD3FC"
|
||||||
|
android:strokeWidth="5"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
</vector>
|
||||||
27
app/src/main/res/drawable/ic_upload.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32dp"
|
||||||
|
android:height="32dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M6.2,18.6 L17.8,18.6 A3.2,3.2 0 0 0 17.9,12.3 A4.6,4.6 0 0 0 8.9,10.6 A3.6,3.6 0 0 0 6.2,18.6 Z"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="1.7"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M12,14.6 L12,8.4"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
<path
|
||||||
|
android:pathData="M9.2,11.2 L12,8.4 L14.8,11.2"
|
||||||
|
android:strokeColor="#FFFFFF"
|
||||||
|
android:strokeWidth="1.8"
|
||||||
|
android:strokeLineCap="round"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:fillColor="#00000000" />
|
||||||
|
</vector>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_background" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_background" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
|
|||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -2,7 +2,11 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
<item name="android:fontFamily">sans</item>
|
<item name="android:fontFamily">sans</item>
|
||||||
<item name="android:windowLightStatusBar">true</item>
|
<item name="android:windowLightStatusBar">false</item>
|
||||||
|
<item name="android:statusBarColor">#1D4ED8</item>
|
||||||
|
<item name="android:windowBackground">#F1F5F9</item>
|
||||||
<item name="android:colorAccent">#2563EB</item>
|
<item name="android:colorAccent">#2563EB</item>
|
||||||
|
<item name="android:windowLightNavigationBar">false</item>
|
||||||
|
<item name="android:navigationBarColor">#F1F5F9</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||