You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android系统级应用如何注入AXIS_RTRIGGER/AXIS_GAS事件?

解决Android系统级应用注入游戏扳机轴(AXIS_RTRIGGER)事件失败的问题

背景

系统级应用持有android.permission.INJECT_EVENTS权限,适配Android 12L及以上版本,采用Kotlin开发,目标是解析硬件按键后通过InputManager向当前界面注入游戏控制器事件。目前左摇杆模拟代码可正常运行,但右扳机(AXIS_RTRIGGER)事件无法被测试应用识别。

问题代码

val eventTime = SystemClock.uptimeMillis()
val pointerProperties = MotionEvent.PointerProperties()
pointerProperties.id = 1
val pointerCoords = MotionEvent.PointerCoords()
pointerCoords.setAxisValue(MotionEvent.AXIS_RTRIGGER, value)
val event = MotionEvent.obtain(
    /* downTime= */ eventTime,
    /* eventTime= */ eventTime,
    /* action= */ MotionEvent.ACTION_MOVE,
    /* pointerCount= */ 1,
    /* pointerProperties= */ arrayOf(pointerProperties),
    /* pointerCoords= */ arrayOf(pointerCoords),
    /* metaState= */ 0,
    /* buttonstate= */ 0,
    /* xPrecision= */ 0.1f,
    /* yPrecision= */0.1f,
    /* deviceId= */mInputManager.getInputDevice(-1).id, //Using -1 as that's a virtual inputDevice
    /* edgeFalgs= */0,
    /* source= */InputDevice.SOURCE_GAMEPAD,
    /* flags= */0
)
mInputManager.injectInputEvent(event, InputEventInjectionSync.WAIT_FOR_RESULT)
event.recycle()

可正常运行的左摇杆参考代码

val eventTime = SystemClock.uptimeMillis()
val event = MotionEvent.obtain(
    /* downTime= */ eventTime,
    /* eventTime= */ eventTime,
    /* action= */ MotionEvent.ACTION_MOVE,
    /* x= */ angle,
    /* y= */ 0.0f,
    /* pressure= */ 1.0f,
    /* size= */ 0f,
    /* metaState= */ 0,
    /* xPrecision= */ 0.1f,
    /* yPrecision= */ 0.1f,
    /* deviceId= */ mInputManager.getInputDevice(-1).id,
    /* edgeFlags= */ 0
)
event.source = InputDevice.SOURCE_JOYSTICK
mInputManager.injectInputEvent(event, InputEventInjectionSync.NONE)

event.recycle()

排查与修复方案

1. 校验轴值范围

MotionEvent.AXIS_RTRIGGER的标准取值范围是0.0f(未按下)到1.0f(完全按下),超出该范围的事件会被系统或测试应用忽略。注入前需确保value被限制在合法区间:

val validValue = value.coerceIn(0.0f, 1.0f)

2. 修正虚拟输入设备获取逻辑

getInputDevice(-1)返回的默认设备可能不支持游戏手柄轴事件。若无法创建自定义虚拟设备,可直接传入设备ID -1(系统会自动映射到合适的虚拟设备),但需确保事件的source与轴类型匹配。

3. 完善MotionEvent构造参数

多指针构造方法需正确设置PointerProperties的必要字段,单指针场景建议使用ID 0,并补充设置工具类型:

val pointerProperties = MotionEvent.PointerProperties().apply {
    id = 0
    toolType = MotionEvent.TOOL_TYPE_UNKNOWN // 游戏控制器无需特定工具类型
}

修正后的完整扳机事件注入代码:

val eventTime = SystemClock.uptimeMillis()
val validValue = value.coerceIn(0.0f, 1.0f)

val pointerProperties = MotionEvent.PointerProperties().apply {
    id = 0
    toolType = MotionEvent.TOOL_TYPE_UNKNOWN
}
val pointerCoords = MotionEvent.PointerCoords().apply {
    setAxisValue(MotionEvent.AXIS_RTRIGGER, validValue)
}

val event = MotionEvent.obtain(
    eventTime,
    eventTime,
    MotionEvent.ACTION_MOVE,
    1,
    arrayOf(pointerProperties),
    arrayOf(pointerCoords),
    0,
    0,
    0.1f,
    0.1f,
    -1, // 直接使用-1作为虚拟设备ID
    0,
    InputDevice.SOURCE_GAMEPAD,
    0
)

// 强制设置事件源,避免构造时的参数覆盖
event.setSource(InputDevice.SOURCE_GAMEPAD)

// 使用WAIT_FOR_RESULT获取注入结果,便于调试
val injectResult = mInputManager.injectInputEvent(event, InputEventInjectionSync.WAIT_FOR_RESULT)
// 结果为1表示注入成功,0表示失败
Log.d("TriggerInjection", "Inject result: $injectResult")

event.recycle()

4. 发送完整事件序列

仅发送ACTION_MOVE可能无法被测试应用正确识别,需模拟完整的按下-移动-抬起序列:

  • 按下:发送ACTION_DOWN事件,轴值设为初始按压值
  • 移动:发送ACTION_MOVE事件,更新轴值
  • 抬起:发送ACTION_UP事件,轴值设为0.0f

示例代码:

fun sendTriggerEvent(value: Float, action: Int) {
    val eventTime = SystemClock.uptimeMillis()
    val validValue = if (action == MotionEvent.ACTION_UP) 0.0f else value.coerceIn(0.0f, 1.0f)

    val pointerProperties = MotionEvent.PointerProperties().apply {
        id = 0
        toolType = MotionEvent.TOOL_TYPE_UNKNOWN
    }
    val pointerCoords = MotionEvent.PointerCoords().apply {
        setAxisValue(MotionEvent.AXIS_RTRIGGER, validValue)
    }

    val event = MotionEvent.obtain(
        eventTime,
        eventTime,
        action,
        1,
        arrayOf(pointerProperties),
        arrayOf(pointerCoords),
        0,
        0,
        0.1f,
        0.1f,
        -1,
        0,
        InputDevice.SOURCE_GAMEPAD,
        0
    )

    event.setSource(InputDevice.SOURCE_GAMEPAD)
    mInputManager.injectInputEvent(event, InputEventInjectionSync.WAIT_FOR_RESULT)
    event.recycle()
}

// 使用方式
sendTriggerEvent(0.5f, MotionEvent.ACTION_DOWN) // 按下扳机到50%
sendTriggerEvent(0.8f, MotionEvent.ACTION_MOVE) // 调整到80%
sendTriggerEvent(0.0f, MotionEvent.ACTION_UP) // 松开扳机

5. 验证事件是否被系统接收

若测试应用仍无响应,可使用Android开发者选项中的输入设备测试器验证事件是否被系统正确接收,排除测试应用自身的兼容性问题。


内容的提问来源于stack exchange,提问作者sumofty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 16:25:53