package com.stand.standapp import android.content.Intent import android.os.Bundle import android.view.KeyEvent import android.view.ViewGroup import android.webkit.ConsoleMessage import android.webkit.WebResourceError import android.webkit.WebResourceRequest import android.webkit.WebView import android.widget.LinearLayout import androidx.appcompat.app.AppCompatActivity import timber.log.Timber import com.just.agentweb.AgentWeb import com.just.agentweb.WebChromeClient import com.just.agentweb.WebViewClient import com.stand.standapp.R import com.stand.standapp.AppConfig import com.stand.standapp.AndroidInterface import com.stand.standapp.UpdateManager class MainActivity : AppCompatActivity() { companion object { private var instance: MainActivity? = null @JvmStatic fun executeJs(script: String) { instance?.let { activity -> activity.runOnUiThread { activity.mAgentWeb?.jsAccessEntrace?.quickCallJs(script) } } } @JvmStatic fun onPttLoginSuccess() { 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") } } @JvmStatic fun showPttConflictDialog() { instance?.runOnUiThread { val ctx = instance ?: return@runOnUiThread androidx.appcompat.app.AlertDialog.Builder(ctx) .setTitle("账号冲突") .setMessage("您的 PTT 账号已在其他设备登录,当前设备已被迫下线。\n请点击确认退出并重新登录。") .setCancelable(false) .setPositiveButton("确认") { _, _ -> val pm = ctx.packageManager val intent = pm.getLaunchIntentForPackage(ctx.packageName) intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) ctx.startActivity(intent) android.os.Process.killProcess(android.os.Process.myPid()) } .show() } } } 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 private val timeoutRunnable = Runnable { showErrorPage() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) instance = this try { setContentView(R.layout.activity_main) val container = findViewById(R.id.container) if (container == null) return findViewById(R.id.btn_retry)?.setOnClickListener { hideErrorPage() mAgentWeb?.webCreator?.webView?.reload() startTimeoutTimer() } // 从配置中获取服务器地址 val serverUrl = AppConfig.getServerUrl(this) val finalUrl = "$serverUrl/example/print_demo.html?t=${System.currentTimeMillis()}" Timber.tag("MainActivity").d("Loading URL: $finalUrl") mAgentWeb = AgentWeb.with(this) .setAgentWebParent(container, LinearLayout.LayoutParams(-1, -1)) .useDefaultIndicator() .setWebChromeClient(object : WebChromeClient() { override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { Timber.tag("WebConsole").d("${consoleMessage?.messageLevel()}: ${consoleMessage?.message()} -- From line ${consoleMessage?.lineNumber()} of ${consoleMessage?.sourceId()}") return super.onConsoleMessage(consoleMessage) } }) .setWebViewClient(object : WebViewClient() { // ... }) .addJavascriptInterface("android", AndroidInterface(this).also { pttInterface = it }) .createAgentWeb() .ready() .go(finalUrl) startTimeoutTimer() // 配置 WebView 属性以支持 SSE 和跨域 mAgentWeb?.let { agent -> try { val webView = agent.webCreator.webView val settings = webView.settings // 允许混合内容 (HTTP/HTTPS) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { settings.mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW } // 允许跨域和存储 settings.allowUniversalAccessFromFileURLs = true settings.allowFileAccessFromFileURLs = true settings.domStorageEnabled = true settings.javaScriptEnabled = true } catch (e: Exception) { Timber.tag("MainActivity").e(e, "WebView settings error") } } // .go("http://152.32.186.199/local/print_demo.html") // 启动时检查更新 UpdateManager(this).checkUpdate() } catch (e: Exception) { Timber.tag("MainActivity").e(e, "Fatal Crash") } } fun getAgentWeb(): AgentWeb? { return mAgentWeb } fun showExitDialog() { // 调用 JS 函数 mAgentWeb?.jsAccessEntrace?.quickCallJs("addExitText") androidx.appcompat.app.AlertDialog.Builder(this) .setTitle("提示") .setMessage("确定要退出应用吗?") .setPositiveButton("确定") { _, _ -> finish() } .setNegativeButton("取消", null) .show() } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (mAgentWeb?.handleKeyEvent(keyCode, event) == true) { return true } if (keyCode == KeyEvent.KEYCODE_BACK) { showExitDialog() return true } return super.onKeyDown(keyCode, event) } override fun onPause() { mAgentWeb?.webLifeCycle?.onPause() super.onPause() } override fun onResume() { mAgentWeb?.webLifeCycle?.onResume() super.onResume() } override fun onDestroy() { mAgentWeb?.webLifeCycle?.onDestroy() cancelTimeoutTimer() super.onDestroy() } private fun startTimeoutTimer() { cancelTimeoutTimer() mHandler.postDelayed(timeoutRunnable, TIMEOUT_MS) } fun cancelTimeoutTimer() { mHandler.removeCallbacks(timeoutRunnable) } private fun showErrorPage() { findViewById(R.id.loading_layout)?.visibility = android.view.View.GONE findViewById(R.id.error_layout)?.visibility = android.view.View.VISIBLE } private fun hideErrorPage() { findViewById(R.id.error_layout)?.visibility = android.view.View.GONE findViewById(R.id.loading_layout)?.visibility = android.view.View.VISIBLE } }