爱踢门之锤子自由截屏快捷键配置(下)

本文深入解析Android系统中如何通过mPolicy->interceptKeyBeforeQueueing和mPolicy->interceptKeyBeforeDispatching函数实现任意快捷键配置,以截图组合键(POWER+VOLUME_DOWN)为例,详细阐述了组合快捷键的延时分发、排他性作用、系统截屏逻辑及个性化实现方式。同时,讨论了单键快捷键的处理机制与特殊处理方式,为开发者提供全面理解系统级快捷键配置的指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快捷键实现

其实说到这,大家估计都不容易了,这么长的代码,同时也会提出质疑”说好的截屏快捷键配置”呢?怎么都没有提及到啊?其实是有提及到的,就是上面的mPolicy-> interceptKeyBeforeQueueing函数和mPolicy->interceptKeyBeforeDispatching。使用这两个函数可以实现任意快捷键配置。其实快捷键应该叫功能键,它和普通键的区别是应用程序没法直接读取到并处理,而是由系统处理了,除了截屏这种组合快捷键外,还有单键快捷键,比如home键,camera键, power键。

下面以系统中默认截屏快捷键实现(KEYCODE_POWER+KEYCODE_VOLUME_DOWN)来分析分析快捷键配置。

多按键快捷键--截屏快捷键实现(power键+volume down)

mPolicy->interceptKeyBeforeQueueing这个函数最终会回调到java层的interceptKeyBeforeQueueing。 

PhoneWindowManager.java
@Override
    public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) { 
        // Handle special keys.
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_VOLUME_MUTE: {
                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                    if (down) {
                        //VolumeDown按键被按下
                        if (isScreenOn && !mVolumeDownKeyTriggered
                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                            mVolumeDownKeyTriggered = true;
                            //检测power键+volume down组合键
                            interceptScreenshotChord();
                        }
                    } else {
                        mVolumeDownKeyTriggered = false;
                        cancelPendingScreenshotChordAction();
                    }
            }
            case KeyEvent.KEYCODE_POWER: {
                result &= ~ACTION_PASS_TO_USER;
                if (down) {
                    if (isScreenOn && !mPowerKeyTriggered
                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                        mPowerKeyTriggered = true;
                        mPowerKeyTime = event.getDownTime();
                        //检测power键+volume down组合键
                        interceptScreenshotChord();
                    }
                }
                break;
            }
        }
        return result;
}

private void interceptScreenshotChord() {
        if (mScreenshotChordEnable && mVolumeDownKeyTriggered 
&& mPowerKeyTriggered && !mVolumeUpKeyTriggered) {
//power键+volumedown按键都已经按下了
            final long now = SystemClock.uptimeMillis();
            if (now <= mVolumeDownKeyTime +
 SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS && 
now <= mPowerKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                    //当前时间离两个按键按下的时间不超过一定时间,组合键被确定是
                    //合法的,发送一个消息来处理该组合键事件
                mVolumeDownKeyConsumedByScreenshotChord = true;
                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
            }
        }
    }
private final Runnable mScreenshotRunnable = new Runnable() {
        @Override
        public void run() {
            takeScreenshot();
        }
    };
    
    // Assume this is called from the Handler thread.
    private void takeScreenshot() {
        synchronized (mScreenshotLock) {
            if (mScreenshotConnection != null) {
                return;
            }
            ComponentName cn = new ComponentName("com.android.systemui",
                    "com.android.systemui.screenshot.TakeScreenshotService");
            Intent intent = new Intent();
            intent.setComponent(cn);
            //启动截屏service,这个就是那个截屏后短暂出现的截屏结果dialog的
//启动器
            if (mContext.bindServiceAsUser(
                intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                mScreenshotConnection = conn;
                mHandler.postDelayed(mScreenshotTimeout, 10000);
            }
        }
    }

    @Override
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
        if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK) == 0) {
            if (mVolumeDownKeyTriggered && !mPowerKeyTriggered) {
                //屏幕组合功能打开,且volumeDown按下了,可以延迟一小段时间分发
//音量键,以等待power键
                final long now = SystemClock.uptimeMillis();
                final long timeoutTime = mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS;
                if (now < timeoutTime) {
                    return timeoutTime - now;
                }
            }
            //如果组合键生效,则这两个键的任何事件直接丢弃,不分发
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mVolumeDownKeyConsumedByScreenshotChord) {
                if (!down) {
                    mVolumeDownKeyConsumedByScreenshotChord = false;
                }
                return -1;
            }
        }
    }

从上面的代码分析可以得出这几个结论:

1)      组合快捷键的实现关键是延时一小短时间分发事件,等待组合键出现

2)      组合快捷键中的键是排他性的,即组合键生效,其本身键的作用失效。

比如如果截屏组合键生效,则其中的volume down事件被分发,即不会弹出音量调节窗体

3)      系统真正截屏的逻辑由TakeScreenshotService实现。

4)      锤子手机的双音量键“人性化”截屏就可以在上面的逻辑里实现

单键快捷键处理

  单键快捷键大部分在interceptKeyBeforeDispatching函数里即系统层处理,比如

KEYCODE_POWER , KEYCODE_HOME, KEYCODE_ENDCALL等,但是另外一类单键快捷键的处理比较特殊,这类事件最后还是被发送到程序端,但是在应用程序的内部代码中处理了,导致用户编码的程序(activity的onKeyDown)里没法读取到并处理,而是,比如很有代表的camera键。由于这个涉及程序端输入事件处理,会在程序端事件处理章节讲完后再单独开辟一章讲一讲这个。


/********************************

* 本文来自博客  “爱踢门”

* 转载请标明出处:http://blog.youkuaiyun.com/itleaks

******************************************/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值