From 71d84f20439ad3718da73a168f8bf1cb94884bda Mon Sep 17 00:00:00 2001 From: Sucan <632190820@qq.com> Date: Tue, 10 Mar 2026 15:39:27 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E4=B8=BB=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E4=B8=8E=E8=AE=BE=E7=BD=AE=E7=95=8C=E9=9D=A2=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=97=A5=E5=BF=97=E7=AE=A1=E7=90=86=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 升级 compileSdk 和 targetSdk 至 36,Java 版本至 17 - 提取字符串资源,实现界面国际化 - 重构主界面布局,移除冗余日志显示,改为按钮导航 - 新增 LogManager 对象,提供日志收集与显示功能 - 增强设置界面,添加 IP 保存、日志显示/隐藏及键盘管理功能 - 优化用户体验,统一界面元素尺寸与交互逻辑 --- app/build.gradle.kts | 10 +- .../lzwcai_terminal_temi/LogManager.kt | 67 +++++++++++++ .../lzwcai_terminal_temi/MainActivity.kt | 41 ++------ .../lzwcai_terminal_temi/SettingsActivity.kt | 59 +++++++++++- app/src/main/res/layout/activity_main.xml | 27 +++--- app/src/main/res/layout/activity_settings.xml | 96 ++++++++++++++----- app/src/main/res/values/strings.xml | 13 +++ 7 files changed, 236 insertions(+), 77 deletions(-) create mode 100644 app/src/main/java/com/example/lzwcai_terminal_temi/LogManager.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 85c88a3..acfa2a3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,12 +5,12 @@ plugins { android { namespace = "com.example.lzwcai_terminal_temi" - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "com.example.lzwcai_terminal_temi" minSdk = 23 - targetSdk = 35 + targetSdk = 36 versionCode = 1 versionName = "1.0" @@ -27,11 +27,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } buildFeatures { viewBinding = true diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/LogManager.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/LogManager.kt new file mode 100644 index 0000000..68f7225 --- /dev/null +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/LogManager.kt @@ -0,0 +1,67 @@ +package com.example.lzwcai_terminal_temi + +import android.util.Log +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import java.io.BufferedReader +import java.io.InputStreamReader + +object LogManager { + private val _logs = MutableLiveData("") + val logs: LiveData = _logs + private val logBuffer = StringBuffer() + private var isReading = false + private val currentPid = android.os.Process.myPid().toString() + + fun startLogcatListener() { + if (isReading) return + isReading = true + + Thread { + try { + // 清除之前的日志缓存 + Runtime.getRuntime().exec("logcat -c") + + // 开始读取日志,过滤当前进程ID + val process = Runtime.getRuntime().exec("logcat -v threadtime") + val reader = BufferedReader(InputStreamReader(process.inputStream)) + + var line: String? + while (reader.readLine().also { line = it } != null) { + line?.let { + if (it.contains(currentPid)) { + // 简单的过滤,只显示包含当前PID的行 + // 可以根据需要进一步处理日志格式 + updateLog(it) + } + } + } + } catch (e: Exception) { + Log.e("LogManager", "Error reading logcat", e) + updateLog("Error reading logs: ${e.message}") + } + }.start() + } + + private fun updateLog(msg: String) { + // 限制日志缓冲区大小,避免内存溢出 + if (logBuffer.length > 50000) { + logBuffer.delete(0, 10000) + } + logBuffer.append("$msg\n") + _logs.postValue(logBuffer.toString()) + } + + fun clearLogs() { + logBuffer.setLength(0) + _logs.postValue("") + // 也可以尝试清除系统日志缓存,但通常需要权限或只能清除应用自己的 + Thread { + try { + Runtime.getRuntime().exec("logcat -c") + } catch (e: Exception) { + e.printStackTrace() + } + }.start() + } +} 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 cda95d1..fcc97d9 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 @@ -4,25 +4,25 @@ import android.annotation.SuppressLint import android.content.Context import android.content.Intent import android.os.Bundle -import android.text.method.ScrollingMovementMethod +import android.util.Log import android.view.WindowManager import androidx.appcompat.app.AppCompatActivity import com.example.lzwcai_terminal_temi.databinding.ActivityMainBinding import com.robotemi.sdk.Robot -import java.util.concurrent.Executors class MainActivity : AppCompatActivity() { private lateinit var robot: Robot private lateinit var binding: ActivityMainBinding - - private val executorService = Executors.newSingleThreadExecutor() @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + + // 启动日志监听 + LogManager.startLogcatListener() // 隐藏软键盘 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) @@ -30,41 +30,14 @@ class MainActivity : AppCompatActivity() { // 获取 Robot 实例 robot = Robot.getInstance() - // 初始化日志视图 - binding.tvLog.movementMethod = ScrollingMovementMethod.getInstance() - // 设置按钮点击事件 binding.btnSettings.setOnClickListener { startActivity(Intent(this, SettingsActivity::class.java)) } - // --- 轻量化处理:移除不必要的监听器 --- - // 移除了 FaceRecognized, Telepresence, GreetMode 等监听器 - // 仅保留基础的权限和运动状态监听作为示例 - - robot.addOnRequestPermissionResultListener { permission, grantResult, _ -> - printLog("Permission: $permission, Result: $grantResult") - } - - robot.addOnMovementStatusChangedListener { type, status -> - printLog("Movement: $type, Status: $status") - } - - // 读取并显示配置的 IP + // 读取配置的 IP 并记录日志 val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE) val ip = prefs.getString("network_ip", "未设置") - printLog("应用启动完成。目标 IP: $ip") + Log.i("MainActivity", "应用启动完成。目标 IP: $ip") } - - private fun printLog(msg: String) { - runOnUiThread { - binding.tvLog.append("$msg\n") - } - } - - override fun onDestroy() { - super.onDestroy() - // 建议在销毁时移除监听器,避免内存泄漏 (虽然 Temi SDK 通常会处理,但显式移除是好习惯) - // robot.removeOn... - } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/lzwcai_terminal_temi/SettingsActivity.kt b/app/src/main/java/com/example/lzwcai_terminal_temi/SettingsActivity.kt index 88f64f3..91a68fb 100644 --- a/app/src/main/java/com/example/lzwcai_terminal_temi/SettingsActivity.kt +++ b/app/src/main/java/com/example/lzwcai_terminal_temi/SettingsActivity.kt @@ -2,6 +2,10 @@ package com.example.lzwcai_terminal_temi import android.content.Context import android.os.Bundle +import android.util.Log +import android.view.View +import android.view.WindowManager +import android.view.inputmethod.InputMethodManager import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.example.lzwcai_terminal_temi.databinding.ActivitySettingsBinding @@ -14,21 +18,68 @@ class SettingsActivity : AppCompatActivity() { super.onCreate(savedInstanceState) binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) + + // 默认隐藏软键盘 + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE) val savedIp = prefs.getString("network_ip", "") binding.etIpAddress.setText(savedIp) + + // 点击外部隐藏键盘 + binding.root.setOnClickListener { + hideKeyboard() + } binding.btnSave.setOnClickListener { + hideKeyboard() val ip = binding.etIpAddress.text.toString().trim() - // Here you could add regex validation for IP address if needed if (ip.isNotEmpty()) { prefs.edit().putString("network_ip", ip).apply() - Toast.makeText(this, "IP Saved: $ip", Toast.LENGTH_SHORT).show() + Toast.makeText(this, getString(R.string.msg_ip_saved, ip), Toast.LENGTH_SHORT).show() + Log.i("SettingsActivity", "IP Saved: $ip") finish() } else { - Toast.makeText(this, "Please enter a valid IP", Toast.LENGTH_SHORT).show() + Toast.makeText(this, getString(R.string.msg_invalid_ip), Toast.LENGTH_SHORT).show() + Log.w("SettingsActivity", "Invalid IP attempt") + } + } + + binding.btnBack.setOnClickListener { + hideKeyboard() + finish() + } + + binding.btnToggleLogs.setOnClickListener { + hideKeyboard() + if (binding.tvLog.visibility == View.VISIBLE) { + binding.tvLog.visibility = View.GONE + binding.btnToggleLogs.text = getString(R.string.btn_show_logs) + } else { + binding.tvLog.visibility = View.VISIBLE + binding.btnToggleLogs.text = getString(R.string.btn_hide_logs) + } + } + + LogManager.logs.observe(this) { logs -> + binding.tvLog.text = logs + // 自动滚动到底部 + binding.tvLog.post { + if (binding.tvLog.layout != null) { + val scrollAmount = binding.tvLog.layout.getLineTop(binding.tvLog.lineCount) - binding.tvLog.height + if (scrollAmount > 0) + binding.tvLog.scrollTo(0, scrollAmount) + else + binding.tvLog.scrollTo(0, 0) + } } } } -} \ No newline at end of file + + private fun hideKeyboard() { + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + currentFocus?.let { + imm.hideSoftInputFromWindow(it.windowToken, 0) + } + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index c610d3e..eebb14f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,25 +7,28 @@ tools:context=".MainActivity"> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_chainStyle="packed" />