快捷键实现
其实说到这,大家估计都不容易了,这么长的代码,同时也会提出质疑”说好的截屏快捷键配置”呢?怎么都没有提及到啊?其实是有提及到的,就是上面的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
******************************************/