package cn.haidong.zzb.rmsys import android.annotation.SuppressLint import android.app.Activity import android.os.Build import android.os.Bundle import android.view.View import android.view.WindowManager import android.webkit.WebResourceRequest import android.webkit.WebSettings import android.webkit.WebView import android.webkit.WebViewClient import android.widget.Toast /** * PAD 端主界面。 * * 设计取舍:**WebView + 同源 HTML**,而不是原生重写 UI。 * 原因:任免审批表是 23 行严格 8 列、竖线偏差控制在 0.1 个百分点的百分比栅格, * 还有两列竖排文字对齐、按字号档整体缩放——这套排版在浏览器里反复调准过。 * 原生重画等于把精度全部重做,必然走样;用同一份 HTML/CSS 在 WebView 里跑, * 才能做到与现有 demo 像素级 1:1。 * * 原生壳只负责四件事: * 1. 启动校验数据包(设备号 + 有效期 + 口令) * 2. 把 SQLite 数据通过 JS Bridge 交给页面 * 3. 保密加固(禁截屏、禁外跳、禁缓存) * 4. 会后清除 */ class MainActivity : Activity() { private lateinit var web: WebView private lateinit var bridge: PackageBridge @SuppressLint("SetJavaScriptEnabled") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 机密会议要求:FLAG_SECURE 让系统层面禁止截屏与录屏 window.setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE ) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES } bridge = PackageBridge(this) web = WebView(this).apply { setBackgroundColor(0xFFF7F5F1.toInt()) settings.apply { javaScriptEnabled = true domStorageEnabled = true allowFileAccess = true // 读 assets/web allowContentAccess = false allowFileAccessFromFileURLs = false // 禁止 file:// 读外部存储 allowUniversalAccessFromFileURLs = false setSupportZoom(false) builtInZoomControls = false displayZoomControls = false mediaPlaybackRequiresUserGesture = true // 保密:不落盘缓存、不留历史 cacheMode = WebSettings.LOAD_NO_CACHE databaseEnabled = false if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { safeBrowsingEnabled = false } } // 长按不弹系统菜单(防复制/分享) isLongClickable = false setOnLongClickListener { true } addJavascriptInterface(bridge, "RmsysNative") webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView, request: WebResourceRequest ): Boolean { val url = request.url.toString() // 只允许包内页面,任何外链一律拦下 return !url.startsWith("file:///android_asset/") } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) // 注入:禁选中、禁长按菜单(CSS 层面再兜一层) view.evaluateJavascript( "document.body.style.webkitUserSelect='none';" + "document.body.style.webkitTouchCallout='none';", null ) } } } // 根布局:去掉系统栏,最大化可用宽度 val root = android.widget.FrameLayout(this) root.addView( web, android.widget.FrameLayout.LayoutParams( android.widget.FrameLayout.LayoutParams.MATCH_PARENT, android.widget.FrameLayout.LayoutParams.MATCH_PARENT ) ) /* 顶部状态栏(时间/电量)画在网页之上,会盖住右上角「设置/退出」导致点不到: 这里给正文让出状态栏高度,并把这条空隙填成与页头一致的红底(白色状态栏图标可读) */ val sbRes = resources.getIdentifier("status_bar_height", "dimen", "android") val sbH = if (sbRes > 0) resources.getDimensionPixelSize(sbRes) else 0 root.setPadding(0, sbH, 0, 0) root.setBackgroundColor(android.graphics.Color.parseColor("#C1272D")) setContentView(root) window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION web.loadUrl("file:///android_asset/web/pad.html") } override fun onBackPressed() { // 会议材料页面不允许用返回键退出到桌面(防误触泄露),二次确认由网页负责 Toast.makeText(this, "请使用页面内「退出」按钮", Toast.LENGTH_SHORT).show() } override fun onPause() { super.onPause() web.onPause() } override fun onResume() { super.onResume() web.onResume() } override fun onDestroy() { try { web.removeJavascriptInterface("RmsysNative") web.destroy() } catch (_: Throwable) { } super.onDestroy() } }