StandAPP/app/src/main/java/com/stand/standapp/MainActivity.kt

225 lines
7.9 KiB
Kotlin

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<LinearLayout>(R.id.container)
if (container == null) return
findViewById<android.widget.Button>(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<android.view.View>(R.id.loading_layout)?.visibility = android.view.View.GONE
findViewById<android.view.View>(R.id.error_layout)?.visibility = android.view.View.VISIBLE
}
private fun hideErrorPage() {
findViewById<android.view.View>(R.id.error_layout)?.visibility = android.view.View.GONE
findViewById<android.view.View>(R.id.loading_layout)?.visibility = android.view.View.VISIBLE
}
}