Android全局监听音量按键事件

Hi,I'm Shendi

最近在编写Android自动化之类的东西,对于触发器我选用了音量键,在这里记录一下


Android全局监听音量按键事件


Android全局监听音量按键事件

有多种方式,但效果和兼容性都不太理想,最终我选择了无障碍+音量广播二合一的方式

无障碍监听按键事件

用户体验

  • 需要手动打开无障碍
  • 在某些手机上有熔断问题(小米实测,按几下后就监听不到了,得重启手机才能继续监听)

实现

在 res/xml 中新建无障碍配置文件 accessibility_service_config.xml,如下

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="50"
    android:canRetrieveWindowContent="true"
    android:accessibilityFlags="flagDefault"
    android:canRequestFilterKeyEvents="true"
    android:canRequestTouchExplorationMode="true"
    android:accessibilityFeedbackType="feedbackAllMask"
/>

上方的 android:canRequestFilterKeyEvents="true" 是必须的,允许无障碍服务接收并过滤按键事件(包括音量键)

新建无障碍服务类,如下

java 复制代码
public class VolumeKeyAccessibilityService extends AccessibilityService {

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();

        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        // 监听按键事件的flags,必须要代码加才能监听到
        info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS;
        info.notificationTimeout = 50;

        setServiceInfo(info);
    }

    @Override
    public boolean onKeyEvent(KeyEvent event) {

        int keyCode = event.getKeyCode();

        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                Log.i("VolumeKey", "音量 + 按下");
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                Log.i("VolumeKey", "音量 + 抬起");
            }
            return true; // 拦截按键(不让系统调音量)
        }

        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            if (event.getAction() == KeyEvent.ACTION_DOWN) {
                Log.i("VolumeKey", "音量 - 按下");
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                Log.i("VolumeKey", "音量 - 抬起");
            }
            return true;
        }

        // 返回 false 表示按键传递给系统
        return false;
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 不需要处理无障碍事件
    }

    @Override
    public void onInterrupt() {

    }
}

清单文件中配置服务

xml 复制代码
<service
    android:name=".VolumeKeyAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:exported="false">

    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>

    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config" />
</service>

广播监听音量变化

我将其作为无障碍无法正确监听的备用选项。

监听的是音量变化,所以当音量为0,按下音量下键不会触发,音量100按下音量上键不会触发。

实现

注册广播接收器

java 复制代码
// 注册广播接收器
IntentFilter filter = new IntentFilter();
// 音量修改事件
filter.addAction("android.media.VOLUME_CHANGED_ACTION");

registerReceiver(receiver, filter);

广播接收器

java 复制代码
/** 最后一次手机的音量 */
private int lastVolume = -1;

private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getAction()) {
            // 音量变动
            case "android.media.VOLUME_CHANGED_ACTION": {
                int newVolume = intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_VALUE", -1);

                if (lastVolume == -1) {
                    lastVolume = newVolume;
                    return;
                }

                if (newVolume > lastVolume) {
                    // 等价于音量上键
                    onVolumeUp();
                } else if (newVolume < lastVolume) {
                    // 等价于音量下键
                    onVolumeDown();
                }

                lastVolume = newVolume;
                break;
            }
        }
    }
};
                

END

相关推荐
y = xⁿ12 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
2601_9620664914 小时前
【Sql Server】Update中的From语句,以及常见更新操作方式
android·java·数据库
用户693717500138420 小时前
曾经安卓开发人手一个的 EventBus,为什么现在没人爱用了?
android·android studio
九皇叔叔1 天前
MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引
android·adb
OriginCoding1 天前
用 AI Agent 协作完成一个 Android TOTP 应用:从需求边界到 v1.0.0
android·ai编程
杉氧1 天前
React 灵魂:深入剖析 Hooks 与副作用(Side Effects)陷阱
android·前端·react native
又见情义1 天前
RK3568 + RTL8211F 网络唤醒(WOL)功能适配全记录
android·网络·驱动开发
用户0934077735141 天前
HarmonyOS WPS Open SDK 实践:registerApp 鉴权与就绪门禁
android·typescript·harmonyos
jike_20261 天前
会议离线录音APP:无网络记录能力对比
android·智能手机·语音识别
always_TT1 天前
【Python 字符串格式化:format() 方法】
android·开发语言·python