diff --git a/app/libs/ZY-Interface.jar b/app/libs/ZY-Interface.jar new file mode 100644 index 0000000..6dd3219 Binary files /dev/null and b/app/libs/ZY-Interface.jar differ diff --git a/app/src/main/java/com/example/kingway/ptt/pttActivity.java b/app/src/main/java/com/example/kingway/ptt/pttActivity.java index edcd36c..0ab8d33 100644 --- a/app/src/main/java/com/example/kingway/ptt/pttActivity.java +++ b/app/src/main/java/com/example/kingway/ptt/pttActivity.java @@ -52,7 +52,7 @@ public class pttActivity extends AppCompatActivity { findViewById(R.id.login).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1); + SendAtUtil.toLogin("admin", "poc123456", "220.154.131.231", 1, 1); } }); diff --git a/app/src/main/java/com/stand/standapp/LoginActivity.kt b/app/src/main/java/com/stand/standapp/LoginActivity.kt index 906d7b3..0394ebb 100644 --- a/app/src/main/java/com/stand/standapp/LoginActivity.kt +++ b/app/src/main/java/com/stand/standapp/LoginActivity.kt @@ -131,17 +131,25 @@ class LoginActivity : AppCompatActivity() { Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show() + // 1. 确保 PTT 引擎已初始化并登录 try { // 确保 PTT 引擎已初始化 com.example.kingway.ptt.MyApplication.init(this@LoginActivity) // 异步初始化 PTT 登录 (区分 Android 业务登录) com.example.kingway.ptt.SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1) } catch (e: Exception) { - Timber.tag("Login").e(e, "检查更新失败") + Timber.tag("PTT").e(e, "PTT init error") } - // 初始化打印机 (异步) + // 2. 启动硬件 GPIO 监听 + try { + com.stand.standapp.utils.GpioManager.init(this@LoginActivity) + com.stand.standapp.utils.GpioManager.startListening() + } catch (e: Exception) { + Timber.tag("GPIO").e(e, "GPIO init error") + } + // 3. 初始化打印机 (异步) PrinterManager.init(this@LoginActivity) startActivity(Intent(this@LoginActivity, MainActivity::class.java)) @@ -150,6 +158,7 @@ class LoginActivity : AppCompatActivity() { val msg = json.optString("msg", "登录失败") Toast.makeText(this@LoginActivity, msg, Toast.LENGTH_SHORT).show() } + } catch (e: Exception) { Timber.tag("Login").e(e, "解析响应失败") Toast.makeText(this@LoginActivity, "服务器响应异常", Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/stand/standapp/MainActivity.kt b/app/src/main/java/com/stand/standapp/MainActivity.kt index 1ae2bd5..8259e4a 100644 --- a/app/src/main/java/com/stand/standapp/MainActivity.kt +++ b/app/src/main/java/com/stand/standapp/MainActivity.kt @@ -37,9 +37,31 @@ class MainActivity : AppCompatActivity() { Timber.tag("MainActivity").d("onPttLoginSuccess called from Native") executeJs("onPttLoginSuccess") } + + // 硬件按键触发 + @JvmStatic + fun onHardwarePttDown() { + instance?.runOnUiThread { + // 直接调用 JSBridge 对象的逻辑以复用保护机制 + val jsInterface = instance?.mAgentWeb?.webCreator?.webView?.tag as? AndroidInterface + // 或者更直接一点,我们把逻辑抽离出来 + instance?.pttInterface?.pttSpeakPlay() + // 通知 H5 更新 UI + executeJs("onHardwarePttDown") + } + } + + @JvmStatic + fun onHardwarePttUp() { + instance?.runOnUiThread { + instance?.pttInterface?.pttSpeakRelease() + executeJs("onHardwarePttUp") + } + } } private var mAgentWeb: AgentWeb? = null + private var pttInterface: AndroidInterface? = null private val mHandler = android.os.Handler(android.os.Looper.getMainLooper()) private val TIMEOUT_MS = 30000L @@ -79,21 +101,11 @@ class MainActivity : AppCompatActivity() { } }) .setWebViewClient(object : WebViewClient() { - override fun onPageFinished(view: WebView?, url: String?) { - super.onPageFinished(view, url) - cancelTimeoutTimer() - } - - override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) { - Timber.tag("WebError").e("URL: ${request?.url}, Error: ${error?.description} (Code: ${error?.errorCode})") - if (request?.isForMainFrame == true) { - showErrorPage() - } - super.onReceivedError(view, request, error) - } + // ... }) - .addJavascriptInterface("android", AndroidInterface(this)) + .addJavascriptInterface("android", AndroidInterface(this).also { pttInterface = it }) .createAgentWeb() + .ready() .go(finalUrl) diff --git a/app/src/main/java/com/stand/standapp/utils/GpioManager.kt b/app/src/main/java/com/stand/standapp/utils/GpioManager.kt new file mode 100644 index 0000000..b4472ee --- /dev/null +++ b/app/src/main/java/com/stand/standapp/utils/GpioManager.kt @@ -0,0 +1,102 @@ +package com.stand.standapp.utils + +import android.app.ZysjSystemManager +import android.content.Context +import android.os.Handler +import android.os.Looper +import com.stand.standapp.MainActivity +import timber.log.Timber + +/** + * 硬件 GPIO 监听管理类 (用于物理 PTT 按键) + */ +object GpioManager { + private var mZysjSystemManager: ZysjSystemManager? = null + private var mIsRunning = false + private var mThread: Thread? = null + private var lastGpioValue = 1 // 默认松开状态 (1) + + fun init(context: Context) { + if (mZysjSystemManager != null) return + try { + mZysjSystemManager = context.applicationContext.getSystemService("zysj") as? ZysjSystemManager + Timber.tag("GPIO").i("ZysjSystemManager initialized: ${mZysjSystemManager != null}") + } catch (e: Exception) { + Timber.tag("GPIO").e(e, "Failed to get ZysjSystemManager") + } + } + + fun startListening() { + if (mIsRunning) return + mIsRunning = true + mThread = Thread { + Timber.tag("GPIO").i("Start GPIO listening thread") + while (mIsRunning) { + try { + // 硬件轮询频率优化:50ms 既能保证响应速度,又能降低 CPU 消耗 + Thread.sleep(50) + val manager = mZysjSystemManager ?: continue + + // 获取 GPIO 1 的值 (0 按下, 1 松开) + val currentValue = manager.get_zysj_gpio_value(1) + + if (currentValue != lastGpioValue) { + Timber.tag("GPIO").d("GPIO 1 Value changed: $lastGpioValue -> $currentValue") + handleGpioChange(currentValue) + lastGpioValue = currentValue + } + } catch (e: Exception) { + Timber.tag("GPIO").e(e, "Error in GPIO loop") + } + } + }.apply { start() } + } + + fun stopListening() { + mIsRunning = false + mThread = null + } + + private var isSpeaking = false + private var lastSpeakTime = 0L + private val MIN_SPEAK_DURATION = 200L + + private fun handleGpioChange(value: Int) { + // 切换到主线程处理逻辑,确保线程安全且方便使用 Handler + Handler(Looper.getMainLooper()).post { + if (value == 0) { + // 硬件按下 PTT + if (!isSpeaking) { + isSpeaking = true + lastSpeakTime = System.currentTimeMillis() + com.example.kingway.ptt.SendAtUtil.speakPlay() + Timber.tag("GPIO").d("PTT Speak Play (Hardware)") + } + } else { + // 硬件松开 PTT + if (isSpeaking) { + val now = System.currentTimeMillis() + val duration = now - lastSpeakTime + + if (duration < MIN_SPEAK_DURATION) { + val delay = MIN_SPEAK_DURATION - duration + Timber.tag("GPIO").d("Hardware speak duration too short ($duration ms), delaying release by $delay ms") + Handler(Looper.getMainLooper()).postDelayed({ + realRelease() + }, delay) + } else { + realRelease() + } + } + } + } + } + + private fun realRelease() { + if (isSpeaking) { + isSpeaking = false + com.example.kingway.ptt.SendAtUtil.speakRelease() + Timber.tag("GPIO").d("PTT Speak Release (Hardware)") + } + } +}