feat: 添加特殊任务模式、生气表情和版本显示
- 新增特殊任务模式开关,带状态指示灯 - 扩展表情系统,增加生气表情并支持眉毛绘制 - 在设置界面显示版本号 - 优化任务管理逻辑,支持特殊任务状态同步 - 改进导航指令命名,将充电功能重命名为 recharge - 增强人机交互,添加基于时间和任务的智能问候 - 优化家庭基站检测逻辑,添加开门/关门提示
This commit is contained in:
@@ -39,7 +39,7 @@ class AnimatedEmojiView @JvmOverloads constructor(
|
||||
private var mouthOpenRatio = 0.1f
|
||||
private var noddingOffset = 0f
|
||||
|
||||
enum class Expression { SMILE, NEUTRAL, TALKING, HAPPY, SAD, WINK }
|
||||
enum class Expression { SMILE, NEUTRAL, TALKING, HAPPY, SAD, WINK, ANGRY }
|
||||
var currentExpression = Expression.SMILE
|
||||
set(value) {
|
||||
field = value
|
||||
@@ -81,6 +81,27 @@ class AnimatedEmojiView @JvmOverloads constructor(
|
||||
canvas.drawCircle(centerX + eyeOffsetX, centerY - eyeOffsetY, eyeRadius, eyePaint)
|
||||
}
|
||||
|
||||
// 2.1 画眉毛 (仅在生气时)
|
||||
if (currentExpression == Expression.ANGRY) {
|
||||
val eyebrowWidth = eyeRadius * 2.5f
|
||||
val eyebrowOffsetY = eyeOffsetY + eyeRadius * 1.5f
|
||||
mouthPaint.strokeWidth = 10f // 眉毛可以细一点
|
||||
|
||||
// 左眉毛
|
||||
canvas.save()
|
||||
canvas.rotate(15f, centerX - eyeOffsetX, centerY - eyebrowOffsetY)
|
||||
canvas.drawLine(centerX - eyeOffsetX - eyebrowWidth / 2, centerY - eyebrowOffsetY, centerX - eyeOffsetX + eyebrowWidth / 2, centerY - eyebrowOffsetY, mouthPaint)
|
||||
canvas.restore()
|
||||
|
||||
// 右眉毛
|
||||
canvas.save()
|
||||
canvas.rotate(-15f, centerX + eyeOffsetX, centerY - eyebrowOffsetY)
|
||||
canvas.drawLine(centerX + eyeOffsetX - eyebrowWidth / 2, centerY - eyebrowOffsetY, centerX + eyeOffsetX + eyebrowWidth / 2, centerY - eyebrowOffsetY, mouthPaint)
|
||||
canvas.restore()
|
||||
|
||||
mouthPaint.strokeWidth = 15f // 恢复嘴巴的宽度
|
||||
}
|
||||
|
||||
// 3. 画嘴巴
|
||||
mouthPaint.style = Paint.Style.STROKE
|
||||
val mouthWidth = radius * 0.6f
|
||||
@@ -97,6 +118,7 @@ class AnimatedEmojiView @JvmOverloads constructor(
|
||||
canvas.drawArc(happyMouthPath, 0f, 180f, false, mouthPaint)
|
||||
}
|
||||
Expression.SAD -> canvas.drawArc(mouthPath, 200f, -140f, false, mouthPaint)
|
||||
Expression.ANGRY -> canvas.drawArc(mouthPath, 200f, -140f, false, mouthPaint)
|
||||
Expression.NEUTRAL -> canvas.drawLine(mouthLeft, mouthTop + mouthHeight / 2, mouthLeft + mouthWidth, mouthTop + mouthHeight / 2, mouthPaint)
|
||||
Expression.WINK -> canvas.drawArc(mouthPath, 20f, 140f, false, mouthPaint)
|
||||
Expression.TALKING -> {
|
||||
|
||||
@@ -25,6 +25,9 @@ import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.cancel
|
||||
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnGoToLocationStatusChangedListener,
|
||||
OnDetectionStateChangedListener, OnReposeStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
OnRequestPermissionResultListener {
|
||||
@@ -43,6 +46,9 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
private val baseFaceSizeDp = 1000f
|
||||
private var currentTask: String = ""
|
||||
|
||||
|
||||
private var closeDoorJob: Job? = null
|
||||
|
||||
private var receptionLocation: String = ""
|
||||
private var receptionText: String = ""
|
||||
private var receptionDestination: String = ""
|
||||
@@ -60,13 +66,16 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
permissionManager = PermissionManager(robot)
|
||||
|
||||
prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
||||
if (prefs.getBoolean("special_task_mode", false)) {
|
||||
currentTask = "special"
|
||||
}
|
||||
|
||||
binding.btnSettings.setOnClickListener {
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
}
|
||||
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.SMILE
|
||||
applyFaceScale(fixedFaceScale)
|
||||
// applyFaceScale(fixedFaceScale) // Use XML constraints for layout
|
||||
|
||||
binding.btnReception.setOnClickListener {
|
||||
val destination = receptionDestination
|
||||
@@ -88,6 +97,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
robot.addOnReposeStatusChangedListener(this)
|
||||
robot.addOnRequestPermissionResultListener(this)
|
||||
prefs.registerOnSharedPreferenceChangeListener(this)
|
||||
robot.constraintBeWith()
|
||||
mqttManager?.connect()
|
||||
}
|
||||
|
||||
@@ -139,13 +149,14 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
Log.i("MainActivity", "TTS started")
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.TALKING
|
||||
}
|
||||
TtsRequest.Status.COMPLETED -> {
|
||||
Log.i("MainActivity", "TTS completed: ${ttsRequest.speech}")
|
||||
TtsRequest.Status.COMPLETED,
|
||||
TtsRequest.Status.CANCELED -> {
|
||||
Log.i("MainActivity", "TTS finished: ${ttsRequest.speech}")
|
||||
if (currentTask == "patrol") {
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.ANGRY
|
||||
} else {
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.SMILE
|
||||
}
|
||||
TtsRequest.Status.CANCELED -> {
|
||||
Log.w("MainActivity", "TTS canceled: ${ttsRequest.speech}")
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.SMILE
|
||||
}
|
||||
TtsRequest.Status.ERROR -> {
|
||||
Log.e("MainActivity", "TTS error: ${ttsRequest.speech}")
|
||||
@@ -173,6 +184,12 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
}
|
||||
|
||||
override fun onDetectionStateChanged(state: Int) {
|
||||
if (currentTask == "patrol" && state == DETECTED) {
|
||||
val ttsRequest = TtsRequest.create("别妨碍我,我正在巡逻呢", false, language = TtsRequest.Language.ZH_CN)
|
||||
robot.speak(ttsRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if (currentTask == "reception" && lastArrivalLocation == receptionLocation) {
|
||||
when (state) {
|
||||
DETECTED -> {
|
||||
@@ -191,9 +208,13 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
}
|
||||
|
||||
// Home Base logic
|
||||
// if (lastArrivalLocation?.lowercase() == "home base") {
|
||||
// when (state) {
|
||||
// DETECTED -> {
|
||||
if (lastArrivalLocation?.lowercase() == "home base") {
|
||||
when (state) {
|
||||
DETECTED -> {
|
||||
closeDoorJob?.cancel()
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.WINK
|
||||
val ttsRequest = TtsRequest.create("正在为你开门,请稍等。", false, language = TtsRequest.Language.ZH_CN)
|
||||
robot.speak(ttsRequest)
|
||||
// mainScope.launch {
|
||||
// HttpManager.workflow_execute(
|
||||
// context = this@MainActivity,
|
||||
@@ -202,8 +223,12 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
// inputs = emptyMap<String, Any>()
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// IDLE -> {
|
||||
}
|
||||
IDLE -> {
|
||||
closeDoorJob = mainScope.launch {
|
||||
delay(5000)
|
||||
val ttsRequest = TtsRequest.create("正准备关门,请小心被夹。", false, language = TtsRequest.Language.ZH_CN)
|
||||
robot.speak(ttsRequest)
|
||||
// mainScope.launch {
|
||||
// HttpManager.workflow_execute(
|
||||
// context = this@MainActivity,
|
||||
@@ -212,13 +237,26 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
// inputs = emptyMap<String, Any>()
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state == DETECTED && currentTask.isEmpty() && lastArrivalLocation?.lowercase() != "home base") {
|
||||
val hour = java.util.Calendar.getInstance().get(java.util.Calendar.HOUR_OF_DAY)
|
||||
val greeting = when (hour) {
|
||||
in 6..11 -> "早上好"
|
||||
in 12..13 -> "中午好"
|
||||
in 14..18 -> "下午好"
|
||||
else -> "晚上好"
|
||||
}
|
||||
val ttsRequest = TtsRequest.create(greeting, false, language = TtsRequest.Language.ZH_CN)
|
||||
robot.speak(ttsRequest)
|
||||
}
|
||||
}
|
||||
|
||||
fun startReceptionMode(location: String, text: String, destination: String) {
|
||||
currentTask = "reception"
|
||||
setCurrentTask("reception")
|
||||
receptionLocation = location
|
||||
receptionText = text
|
||||
receptionDestination = destination
|
||||
@@ -240,6 +278,12 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
fun setCurrentTask(task: String) {
|
||||
currentTask = task
|
||||
Log.i("MainActivity", "Current task set to: $task")
|
||||
// Update expression based on task
|
||||
if (task == "patrol") {
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.ANGRY
|
||||
} else {
|
||||
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.SMILE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onReposeStatusChanged(status: Int, description: String) {
|
||||
@@ -267,6 +311,13 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnG
|
||||
Log.i("MainActivity", "IP address changed, re-initializing MQTT connection.")
|
||||
updateMqttConnection()
|
||||
}
|
||||
if (key == "special_task_mode") {
|
||||
if (sharedPreferences?.getBoolean("special_task_mode", false) == true) {
|
||||
setCurrentTask("special")
|
||||
} else if (currentTask == "special") {
|
||||
setCurrentTask("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMqttConnection() {
|
||||
|
||||
@@ -161,9 +161,9 @@ class MqttManager(
|
||||
private fun handleJsonCommand(obj: JSONObject) {
|
||||
val action = obj.optString("action", obj.optString("cmd", obj.optString("type", ""))).lowercase()
|
||||
when (action) {
|
||||
"charge" -> {
|
||||
"recharge" -> {
|
||||
speak("前往充电桩", "zh")
|
||||
navController.charge()
|
||||
navController.recharge()
|
||||
}
|
||||
"goto" -> {
|
||||
val location = obj.optString("location", obj.optString("target", ""))
|
||||
@@ -184,6 +184,7 @@ class MqttManager(
|
||||
Log.i(TAG, "Repose command sent: $ok")
|
||||
}
|
||||
"stop" -> {
|
||||
(context as? MainActivity)?.setCurrentTask("")
|
||||
navController.stop()
|
||||
stopTts()
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ class NavController(private val robot: Robot) {
|
||||
private val TAG = "NavController"
|
||||
private var playmode = false
|
||||
|
||||
fun charge(): Boolean {
|
||||
fun recharge(): Boolean {
|
||||
playmode = !playmode
|
||||
robot.goTo("home base", playmode)
|
||||
return true
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
@@ -15,11 +16,14 @@ import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.example.lzwcai_terminal_temi.databinding.ActivitySettingsBinding
|
||||
import kotlin.system.exitProcess
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
private var restartAnimator: ValueAnimator? = null
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -28,10 +32,14 @@ class SettingsActivity : AppCompatActivity() {
|
||||
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
|
||||
|
||||
val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
||||
prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
|
||||
val savedIp = prefs.getString("network_ip", "")
|
||||
binding.etIpAddress.setText(savedIp)
|
||||
|
||||
// Set Version Name
|
||||
val versionName = "2603121722"
|
||||
binding.tvVersion.text = getString(R.string.version_prefix, versionName)
|
||||
|
||||
binding.root.setOnClickListener { hideKeyboard() }
|
||||
|
||||
binding.btnSave.setOnClickListener {
|
||||
@@ -54,6 +62,28 @@ class SettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
setupRestartButton()
|
||||
setupSpecialTaskSwitch()
|
||||
}
|
||||
|
||||
private fun setupSpecialTaskSwitch() {
|
||||
val isSpecialTaskMode = prefs.getBoolean("special_task_mode", false)
|
||||
binding.switchSpecialTask.isChecked = isSpecialTaskMode
|
||||
updateStatusIndicator(isSpecialTaskMode)
|
||||
|
||||
binding.switchSpecialTask.setOnCheckedChangeListener { _, isChecked ->
|
||||
prefs.edit().putBoolean("special_task_mode", isChecked).apply()
|
||||
updateStatusIndicator(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStatusIndicator(isSpecialTaskMode: Boolean) {
|
||||
val indicatorColor = if (isSpecialTaskMode) {
|
||||
ContextCompat.getColor(this, android.R.color.holo_red_dark)
|
||||
} else {
|
||||
ContextCompat.getColor(this, android.R.color.holo_green_dark)
|
||||
}
|
||||
val indicatorDrawable = binding.statusIndicator.background as GradientDrawable
|
||||
indicatorDrawable.setColor(indicatorColor)
|
||||
}
|
||||
|
||||
private fun setupRestartButton() {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="5dp" />
|
||||
<solid android:color="#E0E0E0" />
|
||||
<solid android:color="#424242" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dp" />
|
||||
<solid android:color="#4CAF50" />
|
||||
<solid android:color="@color/primary_teal" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
6
app/src/main/res/drawable/status_indicator.xml
Normal file
6
app/src/main/res/drawable/status_indicator.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="@android:color/darker_gray" />
|
||||
<size android:width="24dp" android:height="24dp" />
|
||||
</shape>
|
||||
@@ -1,37 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp"
|
||||
android:background="@color/background_dark"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnSettings"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/btn_settings"
|
||||
android:src="@android:drawable/ic_menu_manage" />
|
||||
android:src="@android:drawable/ic_menu_manage"
|
||||
app:tint="@color/text_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.example.lzwcai_terminal_temi.AnimatedEmojiView
|
||||
android:id="@+id/animatedEmojiView"
|
||||
android:layout_width="500dp"
|
||||
android:layout_height="500dp"
|
||||
android:layout_centerInParent="true" />
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.45"
|
||||
app:layout_constraintWidth_percent="0.6" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnReception"
|
||||
style="@style/Widget.App.Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="100dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:paddingStart="48dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="48dp"
|
||||
android:paddingBottom="16dp"
|
||||
android:text="是的"
|
||||
android:textSize="24sp"
|
||||
android:padding="20dp"
|
||||
android:visibility="gone" />
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,95 +1,208 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<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:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:background="@color/background_dark"
|
||||
tools:context=".SettingsActivity">
|
||||
|
||||
<RelativeLayout
|
||||
<!-- Header -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/headerLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="24dp">
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/surface_dark"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnBack"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/btn_back"
|
||||
android:src="@android:drawable/ic_menu_revert" />
|
||||
android:src="@android:drawable/ic_menu_revert"
|
||||
app:tint="@color/text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSettingsTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/title_settings"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold" />
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/headerLayout">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ipConfigLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/tvSettingsTitle"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etIpAddress"
|
||||
android:layout_width="0dp"
|
||||
<!-- Network Config Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/CardView.App"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="@string/label_ip_config"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
style="@style/Widget.App.TextInputLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/hint_ip_address">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/etIpAddress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/hint_ip_address"
|
||||
android:inputType="number|numberDecimal"
|
||||
android:digits="0123456789."
|
||||
android:minHeight="60dp"
|
||||
android:textSize="24sp" />
|
||||
android:textColor="@color/text_primary" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSave"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:text="@string/btn_save"
|
||||
android:textSize="24sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/restartLayout"
|
||||
style="@style/Widget.App.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/btn_save" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Mode Config Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/CardView.App"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/specialTaskLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="特殊任务模式"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="启用特定场景下的任务逻辑"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/statusIndicator"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/status_indicator" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchSpecialTask"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- System Actions Card -->
|
||||
<androidx.cardview.widget.CardView
|
||||
style="@style/CardView.App"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/ipConfigLayout"
|
||||
android:layout_marginTop="32dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnRestart"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="80dp"
|
||||
android:text="@string/btn_restart_app"
|
||||
android:textSize="18sp" />
|
||||
style="@style/Widget.App.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/btn_restart_app" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/restartProgressBar"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:progress="0"
|
||||
android:progressDrawable="@drawable/custom_progress_bar"
|
||||
android:visibility="invisible" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</ScrollView>
|
||||
<TextView
|
||||
android:id="@+id/tvVersion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="Version: --"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,16 +1,62 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Lzwcaiterminaltemi" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<style name="Theme.Lzwcaiterminaltemi" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorPrimary">@color/primary_teal</item>
|
||||
<item name="colorPrimaryVariant">@color/primary_variant_teal</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_200</item>
|
||||
<item name="colorSecondary">@color/secondary_purple</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">@color/background_dark</item>
|
||||
<!-- Backgrounds -->
|
||||
<item name="android:windowBackground">@color/background_dark</item>
|
||||
<item name="android:textColorPrimary">@color/text_primary</item>
|
||||
<item name="android:textColorSecondary">@color/text_secondary</item>
|
||||
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="materialButtonStyle">@style/Widget.App.Button</item>
|
||||
<item name="textInputStyle">@style/Widget.App.TextInputLayout</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.Button" parent="Widget.MaterialComponents.Button">
|
||||
<item name="backgroundTint">@color/primary_teal</item>
|
||||
<item name="android:textColor">@color/black</item>
|
||||
<item name="cornerRadius">12dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.OutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="strokeColor">@color/primary_teal</item>
|
||||
<item name="android:textColor">@color/primary_teal</item>
|
||||
<item name="cornerRadius">12dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
<item name="boxStrokeColor">@color/primary_teal</item>
|
||||
<item name="boxBackgroundColor">@color/input_background</item>
|
||||
<item name="boxCornerRadiusTopStart">12dp</item>
|
||||
<item name="boxCornerRadiusTopEnd">12dp</item>
|
||||
<item name="boxCornerRadiusBottomStart">12dp</item>
|
||||
<item name="boxCornerRadiusBottomEnd">12dp</item>
|
||||
<item name="android:textColorHint">@color/text_secondary</item>
|
||||
<item name="hintTextColor">@color/primary_teal</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.App.Headline" parent="TextAppearance.MaterialComponents.Headline4">
|
||||
<item name="android:textColor">@color/text_primary</item>
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="CardView.App" parent="Widget.MaterialComponents.CardView">
|
||||
<item name="cardBackgroundColor">@color/surface_dark</item>
|
||||
<item name="cardCornerRadius">16dp</item>
|
||||
<item name="cardElevation">4dp</item>
|
||||
<item name="contentPadding">16dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -7,4 +7,15 @@
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
|
||||
<!-- New Theme Colors -->
|
||||
<color name="background_dark">#121212</color>
|
||||
<color name="surface_dark">#1E1E1E</color>
|
||||
<color name="primary_teal">#03DAC6</color>
|
||||
<color name="primary_variant_teal">#018786</color>
|
||||
<color name="secondary_purple">#BB86FC</color>
|
||||
<color name="text_primary">#FFFFFF</color>
|
||||
<color name="text_secondary">#B0B0B0</color>
|
||||
<color name="divider">#2C2C2C</color>
|
||||
<color name="input_background">#2C2C2C</color>
|
||||
</resources>
|
||||
@@ -10,6 +10,7 @@
|
||||
<string name="btn_back">返回主界面</string>
|
||||
<string name="msg_ip_saved">IP 已保存: %1$s</string>
|
||||
<string name="msg_invalid_ip">请输入有效的 IP 地址</string>
|
||||
<string name="version_prefix">Version: %1$s</string>
|
||||
<string name="btn_random_expression">随机表情</string>
|
||||
<string name="btn_speak">让机器人说话</string>
|
||||
<string name="btn_restart_app">长按重启应用</string>
|
||||
|
||||
@@ -1,16 +1,63 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Lzwcaiterminaltemi" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<style name="Theme.Lzwcaiterminaltemi" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<item name="colorPrimary">@color/primary_teal</item>
|
||||
<item name="colorPrimaryVariant">@color/primary_variant_teal</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondary">@color/secondary_purple</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
|
||||
<item name="android:statusBarColor">@color/background_dark</item>
|
||||
<!-- Backgrounds -->
|
||||
<item name="android:windowBackground">@color/background_dark</item>
|
||||
<item name="android:textColorPrimary">@color/text_primary</item>
|
||||
<item name="android:textColorSecondary">@color/text_secondary</item>
|
||||
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="materialButtonStyle">@style/Widget.App.Button</item>
|
||||
<item name="textInputStyle">@style/Widget.App.TextInputLayout</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.Button" parent="Widget.MaterialComponents.Button">
|
||||
<item name="backgroundTint">@color/primary_teal</item>
|
||||
<item name="android:textColor">@color/black</item>
|
||||
<item name="cornerRadius">12dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
<item name="android:fontFamily">sans-serif-medium</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.OutlinedButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
|
||||
<item name="strokeColor">@color/primary_teal</item>
|
||||
<item name="android:textColor">@color/primary_teal</item>
|
||||
<item name="cornerRadius">12dp</item>
|
||||
<item name="android:textAllCaps">false</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.App.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||
<item name="boxStrokeColor">@color/primary_teal</item>
|
||||
<item name="boxBackgroundColor">@color/input_background</item>
|
||||
<item name="boxCornerRadiusTopStart">12dp</item>
|
||||
<item name="boxCornerRadiusTopEnd">12dp</item>
|
||||
<item name="boxCornerRadiusBottomStart">12dp</item>
|
||||
<item name="boxCornerRadiusBottomEnd">12dp</item>
|
||||
<item name="android:textColorHint">@color/text_secondary</item>
|
||||
<item name="hintTextColor">@color/primary_teal</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.App.Headline" parent="TextAppearance.MaterialComponents.Headline4">
|
||||
<item name="android:textColor">@color/text_primary</item>
|
||||
<item name="android:fontFamily">sans-serif-light</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
|
||||
<style name="CardView.App" parent="Widget.MaterialComponents.CardView">
|
||||
<item name="cardBackgroundColor">@color/surface_dark</item>
|
||||
<item name="cardCornerRadius">16dp</item>
|
||||
<item name="cardElevation">4dp</item>
|
||||
<item name="contentPadding">16dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
Reference in New Issue
Block a user