diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f69a4f1..3f065ee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,6 +16,9 @@
android:supportsRtl="true"
android:theme="@style/Theme.Lzwcaiterminaltemi"
tools:targetApi="31">
+
@@ -31,4 +34,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt
index 356c5f7..9318a31 100644
--- a/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt
+++ b/app/src/main/java/com/example/lzwcai_terminal_temi/MainActivity.kt
@@ -12,14 +12,20 @@ import com.example.lzwcai_terminal_temi.databinding.ActivityMainBinding
import com.robotemi.sdk.Robot
import com.robotemi.sdk.TtsRequest
import com.robotemi.sdk.Robot.TtsListener
+import com.robotemi.sdk.listeners.OnGoToLocationStatusChangedListener
import com.robotemi.sdk.listeners.OnRobotReadyListener
-class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, SharedPreferences.OnSharedPreferenceChangeListener {
+class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, OnGoToLocationStatusChangedListener, SharedPreferences.OnSharedPreferenceChangeListener {
private lateinit var robot: Robot
private lateinit var binding: ActivityMainBinding
private var mqttManager: MqttManager? = null
private lateinit var prefs: SharedPreferences
+ private lateinit var navCon: NavController
+ private var lastArrivalLocation: String? = null
+ private var lastArrivalAt: Long = 0L
+ private val fixedFaceScale = 1.0f
+ private val baseFaceSizeDp = 1000f
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
@@ -30,6 +36,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
LogManager.startLogcatListener()
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
robot = Robot.getInstance()
+ navCon = NavController(robot)
prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
@@ -46,6 +53,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
}
binding.animatedEmojiView.currentExpression = AnimatedEmojiView.Expression.SMILE
+ applyFaceScale(fixedFaceScale)
updateMqttConnection()
}
@@ -54,6 +62,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
super.onStart()
robot.addOnRobotReadyListener(this)
robot.addTtsListener(this)
+ robot.addOnGoToLocationStatusChangedListener(this)
prefs.registerOnSharedPreferenceChangeListener(this)
mqttManager?.connect()
}
@@ -62,13 +71,13 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
super.onStop()
robot.removeOnRobotReadyListener(this)
robot.removeTtsListener(this)
+ robot.removeOnGoToLocationStatusChangedListener(this)
prefs.unregisterOnSharedPreferenceChangeListener(this)
mqttManager?.disconnect()
}
override fun onDestroy() {
super.onDestroy()
- // 确保在应用销毁时彻底释放资源
mqttManager?.disconnect()
LogManager.stopLogcatListener()
Log.i("MainActivity", "All resources released on destroy.")
@@ -108,6 +117,23 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
}
}
+ override fun onGoToLocationStatusChanged(location: String, status: String, descriptionId: Int, description: String) {
+ val normalized = status.lowercase()
+ if (normalized != "complete") {
+ return
+ }
+ val now = System.currentTimeMillis()
+ if (lastArrivalLocation == location && now - lastArrivalAt < 5000L) {
+ return
+ }
+ lastArrivalLocation = location
+ lastArrivalAt = now
+ val text = "已到达$location"
+ val ttsRequest = TtsRequest.create(text, false, language = TtsRequest.Language.ZH_CN)
+ robot.speak(ttsRequest)
+ Log.i("MainActivity", "Arrived at $location, announcement sent.")
+ }
+
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == "network_ip") {
Log.i("MainActivity", "IP address changed, re-initializing MQTT connection.")
@@ -119,7 +145,7 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
mqttManager?.disconnect()
val ip = prefs.getString("network_ip", null)
if (!ip.isNullOrEmpty()) {
- mqttManager = MqttManager(this, ip)
+ mqttManager = MqttManager(this, ip, robot, navCon)
mqttManager?.connect()
Log.i("MainActivity", "MQTT Manager updated with new IP: $ip")
} else {
@@ -127,4 +153,14 @@ class MainActivity : AppCompatActivity(), OnRobotReadyListener, TtsListener, Sha
Log.w("MainActivity", "MQTT Manager disabled: IP address is not set.")
}
}
+
+ private fun applyFaceScale(scale: Float) {
+ val density = resources.displayMetrics.density
+ val sizePx = (baseFaceSizeDp * density * scale).toInt()
+ val params = binding.animatedEmojiView.layoutParams
+ params.width = sizePx
+ params.height = sizePx
+ binding.animatedEmojiView.layoutParams = params
+ binding.animatedEmojiView.requestLayout()
+ }
}
diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt
index ec8e50c..032a43a 100644
--- a/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt
+++ b/app/src/main/java/com/example/lzwcai_terminal_temi/MqttManager.kt
@@ -2,11 +2,19 @@ package com.example.lzwcai_terminal_temi
import android.content.Context
import android.util.Log
+import com.robotemi.sdk.Robot
+import com.robotemi.sdk.TtsRequest
import kotlinx.coroutines.*
import org.eclipse.paho.client.mqttv3.*
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
+import org.json.JSONObject
-class MqttManager(private val context: Context, private val serverIp: String) {
+class MqttManager(
+ private val context: Context,
+ private val serverIp: String,
+ private val robot: Robot,
+ private val navController: NavController
+) {
private var mqttClient: MqttClient? = null
private val TAG = "MqttManager"
@@ -33,10 +41,10 @@ class MqttManager(private val context: Context, private val serverIp: String) {
override fun messageArrived(topic: String?, message: MqttMessage?) {
val payload = String(message?.payload ?: ByteArray(0))
Log.i(TAG, "Message arrived: Topic=$topic, Payload=$payload")
+ handleIncomingMessage(topic, payload)
}
override fun deliveryComplete(token: IMqttDeliveryToken?) {
- // This is not critical for our use case
}
})
} catch (e: MqttException) {
@@ -70,7 +78,7 @@ class MqttManager(private val context: Context, private val serverIp: String) {
private fun scheduleReconnect() {
if (reconnectJob?.isActive == true) {
- return // 如果已经在计划重连,则不再创建新的
+ return
}
reconnectJob = scope.launch {
Log.d(TAG, "Scheduling reconnect in 5 seconds.")
@@ -82,7 +90,7 @@ class MqttManager(private val context: Context, private val serverIp: String) {
fun disconnect() {
scope.launch {
try {
- reconnectJob?.cancel() // 取消任何待处理的重连任务
+ reconnectJob?.cancel()
if (mqttClient?.isConnected == true) {
mqttClient?.disconnect()
Log.i(TAG, "Disconnected from MQTT broker.")
@@ -123,4 +131,76 @@ class MqttManager(private val context: Context, private val serverIp: String) {
}
}
}
+
+ private fun handleIncomingMessage(topic: String?, payload: String) {
+ val trimmed = payload.trim()
+ if (trimmed.isEmpty()) {
+ return
+ }
+ if (!trimmed.startsWith("{")) {
+ Log.w(TAG, "Ignored non-JSON payload on ${topic ?: "unknown"}: $payload")
+ return
+ }
+ try {
+ val obj = JSONObject(trimmed)
+ handleJsonCommand(obj)
+ } catch (e: Exception) {
+ Log.w(TAG, "Invalid JSON payload: $payload")
+ }
+ }
+
+ private fun handleJsonCommand(obj: JSONObject) {
+ val action = obj.optString("action", obj.optString("cmd", obj.optString("type", ""))).lowercase()
+ when (action) {
+ "goto" -> {
+ val location = obj.optString("location", obj.optString("target", ""))
+ goTo(location)
+ }
+ "speak" -> {
+ val text = obj.optString("text", obj.optString("speech", ""))
+ val lang = obj.optString("lang", "")
+ speak(text, lang)
+ }
+ "stop" -> {
+ navController.stop()
+ }
+ "patrol" -> {
+ navController.randomPatrol()
+ }
+ else -> Log.w(TAG, "Unknown command action: $action")
+ }
+ }
+
+ private fun goTo(location: String) {
+ val target = location.trim()
+ if (target.isEmpty()) {
+ Log.w(TAG, "GoTo ignored: empty location")
+ return
+ }
+ val ok = navController.goTo(target)
+ Log.i(TAG, "GoTo command sent: $target, result=$ok")
+ }
+
+ private fun speak(text: String, langCode: String?) {
+ val content = text.trim()
+ if (content.isEmpty()) {
+ Log.w(TAG, "Speak ignored: empty text")
+ return
+ }
+ val language = resolveLanguage(langCode)
+ scope.launch(Dispatchers.Main) {
+ val ttsRequest = TtsRequest.create(content, false, language = language)
+ robot.speak(ttsRequest)
+ Log.i(TAG, "Speak command sent: $content, lang=$language")
+ }
+ }
+
+ private fun resolveLanguage(langCode: String?): TtsRequest.Language {
+ val code = langCode?.trim()?.lowercase().orEmpty()
+ return when (code) {
+ "zh", "zh_cn", "zh-cn" -> TtsRequest.Language.ZH_CN
+ "en", "en_us", "en-us" -> TtsRequest.Language.EN_US
+ else -> TtsRequest.Language.ZH_CN
+ }
+ }
}
diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt
new file mode 100644
index 0000000..7815f36
--- /dev/null
+++ b/app/src/main/java/com/example/lzwcai_terminal_temi/NavController.kt
@@ -0,0 +1,37 @@
+package com.example.lzwcai_terminal_temi
+
+import android.util.Log
+import com.robotemi.sdk.Robot
+
+class NavController(private val robot: Robot) {
+ private val TAG = "NavController"
+
+ fun goTo(location: String): Boolean {
+ robot.goTo(location)
+ return true
+ }
+
+ fun stop(): Boolean {
+ robot.stopMovement()
+ return true
+ }
+
+ fun getAllLocations(): List {
+ return robot.locations
+ }
+
+ fun patrol(locations: List, nonStop: Boolean = false, times: Int = 1, waiting: Int = 3) {
+ robot.patrol(locations, nonStop, times, waiting)
+ }
+
+ fun randomPatrol() {
+ val allLocations = getAllLocations()
+ if (allLocations.size < 3) {
+ return
+ }
+ val patrolCount = (3..minOf(6, allLocations.size)).random()
+ val patrolLocations = allLocations.shuffled().take(patrolCount)
+
+ patrol(patrolLocations, false, 1, 3)
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 4d17619..74761e0 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,42 +6,42 @@
android:padding="16dp"
tools:context=".MainActivity">
-
+
-
+
-
-
-
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="32dp"
+ android:orientation="horizontal">
-
-
+
-
\ No newline at end of file
+
+
+
+
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index dd82c8c..e960264 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -91,38 +91,5 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4866c89..82391e8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,13 +8,9 @@
请输入 IP 地址 (例如 192.168.1.100)
保存
返回主界面
- 显示日志
- 隐藏日志
IP 已保存: %1$s
请输入有效的 IP 地址
- 应用启动完成。目标 IP: %1$s
- 日志将显示在这里...
随机表情
让机器人说话
长按重启应用
-
\ No newline at end of file
+