在Android系统中添加组合键快捷启动功能



启动系统特定功能的组合键的判断应该在系统分发按键消息前处理, 这样从系统运行
的角度来说成本最低。
添加组合键处理需要先搞清楚按键消息在framework中采集 和分发子系统的工作流程,
虽然有android自带的抓屏组合键处理可供参考, 但是理解这一流程还是很重要的,生
搬硬套可能会留下隐含的设计缺陷



Framework的按键预处理机制



framework中按键消息的接收和发送是在两个线程里实现的 。接收线程监听内核发送的
原始按键消息,通过轮训input/event节点实现, 接收线程将读取到的按键消息转换为
android系统定义的按键消息后添加到发送线程的消息队列里 ,发送线程再将消息分发
给android的视图系统,分发是一个比较复杂的过程, 和组合键处理无关这里不作分析

在接收线程添加消息之前和发送线程发送消息之前都会有一个回调给 framework做预处
理的。这种预处理机制就提供了系统处理组合按键的机会, 组合键的使用是策略相关
的,这两个预处理回调函数正是在framework/base/ policy/模块下的
PhoneWindowManager. java文件里面实现的。
两个接口如下:
public  int  interceptKeyBeforeQueueing( KeyEvent  event,  int  policyFlags,
boolean isScreenOn)
public  long interceptKeyBeforeDispatching( WindowState win, KeyEvent event,
int policyFlags)



组合键判断和处理流程



组合键无论用户怎么按,系统接收过程必定是串行的, 两个按键按下的间隔不能太
大,否则视为两个独立的按键;既然按键消息的串行接收的, 先来的按键消息就会被
添加到发送队列里面,这就要求发送前有一个确认机制, 如果组合键条件满足,则触
发对应功能,并取消发送。

预处理接口的处理流程简单描述:
接收预处理( interceptKeyBeforeQueueing):
记录组合键触发状态,触发时间,判断触发条件是否满足, 如果满足则启动快捷功
能,并标记按键消息被消费

发送预处理( interceptKeyBeforeDispatching)
如果组合键触发时间没有溢出,则推迟发送;如果被消费, 则取消发送

组合键长按确认处理
如果组合按键需要长按确认,避免误操作?!,如长按‘ power’和‘volume-’启动抓屏
功能,则在判断触发条件满足后不是直接启动快捷功能, 而是发起一个延时启动请
求,如果延时请求发出前,按键抬起, 则取消正在等待的延时启动请求,并清除触发
标识。



在系统中增加组合键启动一个特定应用的设计方案实例:




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 otChord;  // 标识volume
已经被系统消费
     // 变量名套用了android的抓屏功能原始设计, 实际上按键消费标识的判断使用
和消费对象无关
     private boolean mVolumeDownKeyConsumedByScreen shotChord;

// 组合键触发判断, 在接收预处理中调用
+    private void interceptQuickMemoChord() {
         // 音量键同时按下,并且电源键没有按下
+          if   (   mVolumeDownKeyTriggered   &&   !mPowerKeyTriggered   &&
mVolumeUpKeyTriggered) {
+            final long now = SystemClock.uptimeMillis();
+            try { // 查询要启动应用是否安装,如果应用没有安装直接返回
+                               mContext.getPackageManager(). getPackageInfo
("com.hisense.quickmemo",0);
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "no found com.hisense.quickmemo");
+                return;
+            }
+                   if       (now       <=       mVolumeDownKeyTime       +
SCREENSHOT_CHORD_DEBOUNCE_ DELAY_MILLIS
+                          &&      now      <=      mVolumeUpKeyTime      +
SCREENSHOT_CHORD_DEBOUNCE_ DELAY_MILLIS) {
+            // 按键触发事件在有效期内
+                mVolumeDownKeyConsumedByScreen shotChord = true;
+                mVolumeUpKeyConsumedByScreensh otChord = true;
+                mHandler.post( mQuickMemoChordPress);
/*    如果需要长按确认, 这里使用延时处理接口postDelayed, 指定一个长按确认时
间,并在按键up消息里面增加取消判断处理*/
+            }
+        }
+    }
+    // 启动快捷应用
+    private final Runnable mQuickMemoChordPress = new Runnable() {
+        public void run() {
+                       Log.d(TAG, "mQuickMemoChordLongPress");
+
+                       Intent intent = new Intent();
+                       intent.setFlags(Intent.FLAG_ ACTIVITY_NEW_TASK);
+                       intent.setAction("android. intent.action.MAIN");
+                       intent.setClassName("com. hisense.quickmemo",
+
"com.hisense.quickmemo. activities.QuickMemoActivity") ;
+                       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_ DELAY_MILLIS;
                 if (now < timeoutTime) {
                     return timeoutTime - now;
                }
             }
          // 如果按键键消息被系统消费,则取消发送
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
                    && mVolumeDownKeyConsumedByScreen shotChord) {
                if (!down) {
                    mVolumeDownKeyConsumedByScreen shotChord = false;
                }
                return -1;
            }
+            if (keyCode == KeyEvent.KEYCODE_VOLUME_UP
+                    && mVolumeUpKeyConsumedByScreensh otChord) {
+                if (!down) {
+                    mVolumeUpKeyConsumedByScreensh otChord = false;
+                }
+                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 shotChord      =
false;
                             mVolumeDownKeyTime = event.getDownTime();
                             cancelPendingPowerKeyAction();
                             interceptScreenshotChord();
+                            interceptQuickMemoChord();
                         }
                     } else {
                         mVolumeDownKeyTriggered = false;
                         cancelPendingScreenshotChordAc tion();
                     }
                 } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
                     if (down) {
@@   -3740,10   +3798,14  @@  public  class  PhoneWindowManager  implements
WindowManagerPolicy {
                             mVolumeUpKeyTriggered = true;
                             cancelPendingPowerKeyAction();
                             cancelPendingScreenshotChordAc tion();
+                            mVolumeUpKeyConsumedByScreensh otChord = false;
+                            mVolumeUpKeyTime = event.getDownTime();
+                            interceptQuickMemoChord();
                         }
                     } else {
                         mVolumeUpKeyTriggered = false;
                         cancelPendingScreenshotChordAc tion();
                     }
                 }
 (END)

你可能感兴趣的:(Android,组合键)