启动系统特定功能的组合键的判断应该在系统分发按键消息前处理,
的角度来说成本最低。
添加组合键处理需要先搞清楚按键消息在framework中采集
虽然有android自带的抓屏组合键处理可供参考,
搬硬套可能会留下隐含的设计缺陷 。
Framework的按键预处理机制
framework中按键消息的接收和发送是在两个线程里实现的
原始按键消息,通过轮训input/event节点实现,
android系统定义的按键消息后添加到发送线程的消息队列里
给android的视图系统,分发是一个比较复杂的过程,
在接收线程添加消息之前和发送线程发送消息之前都会有一个回调给
理的。这种预处理机制就提供了系统处理组合按键的机会,
的,这两个预处理回调函数正是在framework/base/
PhoneWindowManager.
两个接口如下:
public int interceptKeyBeforeQueueing(
boolean isScreenOn)
public long interceptKeyBeforeDispatching(
int policyFlags)
组合键判断和处理流程
组合键无论用户怎么按,系统接收过程必定是串行的,
大,否则视为两个独立的按键;既然按键消息的串行接收的,
添加到发送队列里面,这就要求发送前有一个确认机制,
发对应功能,并取消发送。
预处理接口的处理流程简单描述:
接收预处理(
记录组合键触发状态,触发时间,判断触发条件是否满足,
能,并标记按键消息被消费
发送预处理(
如果组合键触发时间没有溢出,则推迟发送;如果被消费,
组合键长按确认处理
如果组合按键需要长按确认,避免误操作?!,如长按‘
功能,则在判断触发条件满足后不是直接启动快捷功能,
求,如果延时请求发出前,按键抬起,
标识。
在系统中增加组合键启动一个特定应用的设计方案实例:
Volume+和Volume-同时按下启动,不需要长按确认。
代码修改:
public class PhoneWindowManager implements WindowManagerPolicy {
private boolean mVolumeUpKeyTriggered; // 按键触发
(按下)标志
private boolean mPowerKeyTriggered;
private boolean mVolumeDownKeyTriggered;
+ private long mVolumeUpKeyTime; // 记录volume
键按下的时间
private long mVolumeDownKeyTime;
private long mPowerKeyTime;
+ private boolean mVolumeUpKeyConsumedByScreensh
已经被系统消费
// 变量名套用了android的抓屏功能原始设计,
和消费对象无关
private boolean mVolumeDownKeyConsumedByScreen
// 组合键触发判断, 在接收预处理中调用
+ private void interceptQuickMemoChord() {
// 音量键同时按下,并且电源键没有按下
+ if ( mVolumeDownKeyTriggered && !mPowerKeyTriggered &&
mVolumeUpKeyTriggered) {
+ final long now = SystemClock.uptimeMillis();
+ try { // 查询要启动应用是否安装,如果应用没有安装直接返回
+ mContext.getPackageManager().
("com.hisense.quickmemo",0);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "no found com.hisense.quickmemo");
+ return;
+ }
+ if (now <= mVolumeDownKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_
+ && now <= mVolumeUpKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_
+ // 按键触发事件在有效期内
+ mVolumeDownKeyConsumedByScreen
+ mVolumeUpKeyConsumedByScreensh
+ mHandler.post(
/* 如果需要长按确认,
间,并在按键up消息里面增加取消判断处理*/
+ }
+ }
+ }
+ // 启动快捷应用
+ private final Runnable mQuickMemoChordPress = new Runnable() {
+ public void run() {
+ Log.d(TAG, "mQuickMemoChordLongPress");
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_
+ intent.setAction("android.
+ intent.setClassName("com.
+
"com.hisense.quickmemo.
+ mContext.startActivity(intent)
+ }
+ };
+
// 发送预处理
@@ -1951,7 +2000,7 @@ public class PhoneWindowManager implements
WindowManagerPolicy {
if (mScreenshotChordEnabled && (flags & KeyEvent.FLAG_FALLBACK)
== 0) {
// 推迟按键消息发送
+ if ((mVolumeUpKeyTriggered || mVolumeDownKeyTriggered) &&
!mPowerKeyTriggered) {
final long now = SystemClock.uptimeMillis();
final long timeoutTime = mVolumeDownKeyTime +
SCREENSHOT_CHORD_DEBOUNCE_
if (now < timeoutTime) {
return timeoutTime - now;
}
}
// 如果按键键消息被系统消费,则取消发送
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
&& mVolumeDownKeyConsumedByScreen
if (!down) {
mVolumeDownKeyConsumedByScreen
}
return -1;
}
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ && mVolumeUpKeyConsumedByScreensh
+ if (!down) {
+ mVolumeUpKeyConsumedByScreensh
+ }
+ return -1;
+ }
}
// 接收预处理
@@ -3728,10 +3784,12 @@ public class PhoneWindowManager implements
WindowManagerPolicy {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (isScreenOn && !mVolumeDownKeyTriggered
&& (event.getFlags() &
KeyEvent.FLAG_FALLBACK) == 0) {
mVolumeDownKeyTriggered = true;
mVolumeDownKeyTime = event.getDownTime();
mVolumeDownKeyConsumedByScreen
false;
mVolumeDownKeyTime = event.getDownTime();
cancelPendingPowerKeyAction();
interceptScreenshotChord();
+ interceptQuickMemoChord();
}
} else {
mVolumeDownKeyTriggered = false;
cancelPendingScreenshotChordAc
}
} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
if (down) {
@@ -3740,10 +3798,14 @@ public class PhoneWindowManager implements
WindowManagerPolicy {
mVolumeUpKeyTriggered = true;
cancelPendingPowerKeyAction();
cancelPendingScreenshotChordAc
+ mVolumeUpKeyConsumedByScreensh
+ mVolumeUpKeyTime = event.getDownTime();
+ interceptQuickMemoChord();
}
} else {
mVolumeUpKeyTriggered = false;
cancelPendingScreenshotChordAc
}
}
(END)