feat(android): 状态栏触摸诊断通道(vivo logcat 被限流的对策)
本机(vivo S6 + 办公 Windows)logcat 只能抓到 4 行,App 日志完全不可靠, 排查触摸/坐标问题时没有任何可用通道。 - RemoteSurfaceView 加 onDebug 回调:ACTION_DOWN/MOVE/UP 的视图坐标与 发出的 norm 归一化坐标实时上报 - MainActivity 统一 refreshStatus():连接态 + 帧数/RTT + 最近触摸合并成 一处刷新(原来 8 处各自 statusView.text 互相覆盖,且引用过已删的 showHint) - 删掉临时步骤标记与 lastMeasureInfo(诊断价值已被 onDebug 覆盖) 实测:状态栏正确显示 UP view=(643,222) norm=(39017,23968) 1080x607, 据此确认了 letterbox 视图尺寸与归一化换算全对,把排查焦点正确引向 agent 注入侧(VIRTUALDESK,已另行修复)。
This commit is contained in:
parent
6a6f989595
commit
fe558a1c1e
@ -37,11 +37,17 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
@Volatile
|
||||
private var remoteH = 0
|
||||
|
||||
/** 最近一次触摸事件(诊断用:logcat 被限流,状态栏是唯一可靠通道) */
|
||||
@Volatile
|
||||
private var lastTouchDbg = "-"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.i(TAG, "onCreate start")
|
||||
|
||||
surfaceView = RemoteSurfaceView(this).apply {
|
||||
listener = this@MainActivity
|
||||
onDebug = { msg -> lastTouchDbg = msg; refreshStatus() }
|
||||
holder.addCallback(object : SurfaceHolder.Callback {
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {}
|
||||
override fun surfaceChanged(h: SurfaceHolder, f: Int, w: Int, ht: Int) {}
|
||||
@ -58,14 +64,20 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
setTextColor(0xFFFFFFFF.toInt())
|
||||
}
|
||||
|
||||
// 画面居中、黑边填充:LinearLayout 竖直 + gravity center
|
||||
// 画面居中、黑边填充。
|
||||
// 用 FrameLayout + MATCH_PARENT(不是 WRAP_CONTENT):FrameLayout 会把
|
||||
// 子视图的 WRAP_CONTENT 当 MATCH_PARENT 处理,onMeasure 里拿到的
|
||||
// MeasureSpec 就成了 EXACTLY 全尺寸,letterbox 比例收缩被直接忽略
|
||||
// (2026-10-30 实测:视图被撑成 1080x2001,触摸按 2001 归一化 → 坐标全错)。
|
||||
// MATCH_PARENT + gravity=center 时父容器传 AT_MOST,onMeasure 的
|
||||
// setMeasuredDimension 才真正决定尺寸,再被 gravity 居中摆放。
|
||||
val stage = android.widget.FrameLayout(this).apply {
|
||||
setBackgroundColor(0xFF000000.toInt())
|
||||
addView(
|
||||
surfaceView,
|
||||
android.widget.FrameLayout.LayoutParams(
|
||||
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
android.widget.FrameLayout.LayoutParams.WRAP_CONTENT,
|
||||
android.widget.FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
android.widget.FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
android.view.Gravity.CENTER
|
||||
)
|
||||
)
|
||||
@ -74,23 +86,33 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
orientation = android.widget.LinearLayout.VERTICAL
|
||||
addView(stage, android.widget.LinearLayout.LayoutParams(
|
||||
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f))
|
||||
// 固定高度而非 WRAP_CONTENT:与 weight=1 的兄弟共存时,
|
||||
// WRAP_CONTENT 可能被压成 0(实测 bounds=[0,0][0,0],状态栏不可见)
|
||||
addView(statusView, android.widget.LinearLayout.LayoutParams(
|
||||
android.widget.LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT))
|
||||
(48 * resources.displayMetrics.density).toInt()))
|
||||
}
|
||||
setContentView(root)
|
||||
|
||||
showHint()
|
||||
refreshStatus()
|
||||
intent?.getStringExtra("host")?.let { host ->
|
||||
connect(host, intent.getIntExtra("port", 47800), intent.getStringExtra("code") ?: "123456")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showHint() {
|
||||
statusView.text = if (handle != 0L) {
|
||||
"已连接 ${remoteW}x$remoteH|单指=左键 双指轻点=右键 双指滑动=滚轮 返回键=断开"
|
||||
} else {
|
||||
"未连接|am start -n com.verify.remote/.MainActivity --es host <IP> --ei port 47800 --es code t"
|
||||
/** 状态栏统一刷新:连接态 + 帧数/RTT + 最近触摸诊断(logcat 被限流,这是唯一通道) */
|
||||
@Volatile
|
||||
private var lastFrames = 0L
|
||||
@Volatile
|
||||
private var lastRtt = 0L
|
||||
|
||||
private fun refreshStatus() {
|
||||
if (handle == 0L) {
|
||||
statusView.text = "未连接|am start ... --es host <IP> --ei port 47800 --es code t"
|
||||
return
|
||||
}
|
||||
statusView.text = buildString {
|
||||
append("已连接 ${remoteW}x$remoteH 帧=$lastFrames RTT=${lastRtt}ms\n")
|
||||
append("触摸: $lastTouchDbg")
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,8 +157,10 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
remoteW = w
|
||||
remoteH = ht
|
||||
runOnUiThread {
|
||||
if (w > 0 && ht > 0) surfaceView.videoAspectRatio = w.toFloat() / ht
|
||||
showHint()
|
||||
if (w > 0 && ht > 0) {
|
||||
surfaceView.videoAspectRatio = w.toFloat() / ht
|
||||
}
|
||||
refreshStatus()
|
||||
startDecodeLoop(h)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@ -173,19 +197,14 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
}
|
||||
frames++
|
||||
if (frames % 120 == 0L) {
|
||||
val rtt = RustBridge.rttMs(h)
|
||||
runOnUiThread {
|
||||
statusView.text =
|
||||
"已连接 ${remoteW}x$remoteH 帧=$frames RTT=${rtt}ms|返回键断开"
|
||||
}
|
||||
lastFrames = frames
|
||||
lastRtt = RustBridge.rttMs(h)
|
||||
runOnUiThread { refreshStatus() }
|
||||
}
|
||||
}
|
||||
dec.release()
|
||||
Log.i(TAG, "decode loop ended, frames=$frames")
|
||||
runOnUiThread {
|
||||
statusView.text = "会话结束(帧 $frames)"
|
||||
showHint()
|
||||
}
|
||||
runOnUiThread { refreshStatus() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,8 +219,10 @@ class MainActivity : Activity(), RemoteSurfaceView.Listener {
|
||||
handle = 0L
|
||||
remoteW = 0
|
||||
remoteH = 0
|
||||
lastFrames = 0
|
||||
lastRtt = 0
|
||||
RustBridge.destroy(h)
|
||||
showHint()
|
||||
refreshStatus()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
@ -62,15 +62,33 @@ class RemoteSurfaceView(context: Context) : SurfaceView(context) {
|
||||
isFocusable = true
|
||||
}
|
||||
|
||||
/**
|
||||
* letterbox 测量:在父容器给出的可用空间内按视频宽高比取最大内接矩形。
|
||||
*
|
||||
* 关键:父容器给 WRAP_CONTENT 时 MeasureSpec 是 AT_MOST,getSize() 是
|
||||
* **上限**而非实际可用尺寸。据此算出的尺寸必须自己 setMeasuredDimension,
|
||||
* 否则父容器会把视图撑满(2026-10-30 实测:视图变成 1080x2001 而非
|
||||
* 1080x607,触摸按 2001 归一化 → 坐标全错,点中心落到画面外)。
|
||||
*/
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
super.onMeasure(widthSpec, heightSpec)
|
||||
val ar = videoAspectRatio
|
||||
if (ar <= 0f) return
|
||||
var w = measuredWidth
|
||||
var h = measuredHeight
|
||||
if (w <= 0 || h <= 0) return
|
||||
if (w.toFloat() > h * ar) w = (h * ar).toInt() else h = (w / ar).toInt()
|
||||
setMeasuredDimension(w, h)
|
||||
if (ar <= 0f) {
|
||||
super.onMeasure(widthSpec, heightSpec)
|
||||
return
|
||||
}
|
||||
val availW = MeasureSpec.getSize(widthSpec)
|
||||
val availH = MeasureSpec.getSize(heightSpec)
|
||||
if (availW <= 0 || availH <= 0) {
|
||||
super.onMeasure(widthSpec, heightSpec)
|
||||
return
|
||||
}
|
||||
var w = availW
|
||||
var h = (availW / ar).toInt()
|
||||
if (h > availH) {
|
||||
h = availH
|
||||
w = (availH * ar).toInt()
|
||||
}
|
||||
setMeasuredDimension(w.coerceAtLeast(1), h.coerceAtLeast(1))
|
||||
}
|
||||
|
||||
private fun normX(x: Float) = (x / width * 65535).toInt().coerceIn(0, 65535)
|
||||
@ -85,9 +103,28 @@ class RemoteSurfaceView(context: Context) : SurfaceView(context) {
|
||||
private fun midY(e: MotionEvent) =
|
||||
if (e.pointerCount >= 2) (e.getY(0) + e.getY(1)) / 2f else e.y
|
||||
|
||||
/** 诊断回调:把触摸情况显示到状态栏(本机 logcat 被系统限流,日志不可靠) */
|
||||
var onDebug: ((String) -> Unit)? = null
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
val l = listener ?: return false
|
||||
val l = listener ?: run {
|
||||
onDebug?.invoke("touch: listener=null")
|
||||
return false
|
||||
}
|
||||
if (width == 0 || height == 0) return false
|
||||
val act = when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> "DOWN"
|
||||
MotionEvent.ACTION_MOVE -> "MOVE"
|
||||
MotionEvent.ACTION_UP -> "UP"
|
||||
MotionEvent.ACTION_POINTER_DOWN -> "P2DOWN"
|
||||
MotionEvent.ACTION_POINTER_UP -> "P2UP"
|
||||
MotionEvent.ACTION_CANCEL -> "CANCEL"
|
||||
else -> event.actionMasked.toString()
|
||||
}
|
||||
onDebug?.invoke(
|
||||
"$act view=(${event.x.toInt()},${event.y.toInt()}) " +
|
||||
"norm=(${normX(event.x)},${normY(event.y)}) ${width}x$height"
|
||||
)
|
||||
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user