需求:定制物理按鍵可以轉(zhuǎn)換成別的按鍵/打開(kāi)其他應(yīng)用/發(fā)送intent
例如:
按下 VOLUME_DOWN -> Back key
按下 VOLUME_DOWN -> 打開(kāi)系統(tǒng)某個(gè)應(yīng)用
按下 VOLUME_DOWN -> 發(fā)送特定intent
簡(jiǎn)單分析:
首先想到的是PhoneWindowManager的interceptKeyBeforeQueueing 去攔截按鍵,其次:
一. 需要系統(tǒng)級(jí)service及數(shù)據(jù)庫(kù)保證按鍵轉(zhuǎn)換的映射,保證重啟仍然生效
二. 需要一個(gè)APP支持轉(zhuǎn)換邏輯
我們都知道Android系統(tǒng)的啟動(dòng)是先加載Linux kernel,再啟動(dòng)init進(jìn)程,創(chuàng)建SystemServer,然后在SystemServer 啟動(dòng)各種Service讓系統(tǒng)跑起來(lái)。所以我們也要?jiǎng)?chuàng)建一個(gè)系統(tǒng)Service. 由于代碼量的問(wèn)題,就不全部上傳了。
1). 自定義系統(tǒng)service
frameworks/base/core/java/android/app/hijackbutton/IHiJackService.aidl
package android.app.hijackbutton;
import android.view.KeyEvent;
import android.app.hijackbutton.HijackingKeys;
import android.app.hijackbutton.HiJackData;
/**
* {@hide}
*/
interface IHiJackService {
HijackingKeys[] getHijackingKeys(); //獲取需要轉(zhuǎn)換的按鍵 供上層app顯示【1】
int setAllHiJackData(in HiJackData[] dataList);//設(shè)置鍵值對(duì)應(yīng)/打開(kāi)系統(tǒng)某個(gè)應(yīng)用/發(fā)送特定intent【2】
HiJackData[] getAllHiJackData(); //得到所有的key的鍵值對(duì)應(yīng),供上層app顯示【3】
int hijackingKey(inout KeyEvent event, boolean useCache); //按鍵轉(zhuǎn)換【4】
}
自定義系統(tǒng)service實(shí)現(xiàn):
frameworks/base/services/core/java/com/android/server/hijackbutton/HiJackService.java
//功能1:創(chuàng)建數(shù)據(jù)庫(kù),并且初始化HijackManager 即初始化數(shù)據(jù)庫(kù)。
public HiJackService(Context context) {
mContext = context; mThread = new HandlerThread(TAG);
mThread.start();
mHandler = new Handler(mThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case INIT_MSG: {
if (mHiJackManager != null) {
mHiJackManager.initialization();
}
break;
}
default: {
break;
} } } };
mHiJackManager = new HiJackManager(context, new HiJackDBHelper(context));
mHiJackManager.open();
mHandler.sendEmptyMessageDelayed(INIT_MSG, 500);
}
// 功能2: 按鍵轉(zhuǎn)換【4】
public int hijackingKey(KeyEvent event, boolean useCache) throws RemoteException { hijackingMappingKey(event, useCache);
return event.getKeyCode();
}
private boolean hijackingMappingKey(KeyEvent event, boolean useCache) { HiJackData data = mHiJackManager.selData(event.getKeyCode());
returnKeyCode = data.getConvertKeyCode();
event.hijackingKeyCode(returnKeyCode); //轉(zhuǎn)換按鍵
return true;
}
KeyEvent的處理:
frameworks/base/core/java/android/view/KeyEvent.java public KeyEvent hijackingKeyCode(int code) {
//直接轉(zhuǎn)換了按鍵
mKeyCode = code;
return this;
}
被轉(zhuǎn)換的按鍵:
//需要被轉(zhuǎn)換的按鍵 【1】
frameworks/base/core/java/android/app/hijackbutton/HijackingKeys.aidl
frameworks/base/core/java/android/app/hijackbutton/HijackingKeys.java
關(guān)聯(lián)數(shù)據(jù)庫(kù):
//和HiJackService 相關(guān)聯(lián)的數(shù)據(jù)庫(kù).
frameworks/base/core/java/android/app/hijackbutton/HiJackDBHelper.java
//定義的字段
public static final String HIJACK_ID = "_id";
public static final String HIJACK_LABEL = "_label";
public static final String HIJACK_DEFAULT_KEYCODE = "_default_keycode"; //默認(rèn)keycode
public static final String HIJACK_DEFAULT_SYMBOL = "_default_symbol"; //默認(rèn)按鍵名
public static final String HIJACK_CONVERT_KEYCODE = "_convert_keycode";//轉(zhuǎn)換后keycode
public static final String HIJACK_CONVERT_SYMBOL = "_convert_symbol";? //轉(zhuǎn)換后按鍵名
數(shù)據(jù)庫(kù)的對(duì)象封裝:
frameworks/base/core/java/android/app/hijackbutton/HiJackData.aidl
frameworks/base/core/java/android/app/hijackbutton/HiJackData.java
// 對(duì)HiJackDBHelper的對(duì)象封裝。
private long mID = 0;
private String mLabel = "";
private int mDefaultKeyCode = 0;
private String mDefaultSymbol = "";
private int mConvertKeyCode = 0;
private String mConvertSymbol = "";
添加service到SystemServer:
frameworks/base/services/java/com/android/server/SystemServer.java
//系統(tǒng)服務(wù)的添加 startOtherServices():
traceBeginAndSlog("StartHijackService");
mHijack = new HiJackService(context);
ServiceManager.addService(Context.HIJACK_SERVICE, mHijack);
traceEnd();
這樣就完成了系統(tǒng)service的添加。
最后在關(guān)鍵點(diǎn)的使用這個(gè)service的hijackingKey()的轉(zhuǎn)換功能:
frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
interceptKeyBeforeDispatching 中調(diào)用 【4】
frameworks/base/core/java/android/view/ViewRootImpl.java
ViewPreImeInputStage.processKeyEvent 調(diào)用 【4】
轉(zhuǎn)換按鍵APP:
初始界面:

gpio-key 被轉(zhuǎn)換的按鍵:?
這個(gè)list 通過(guò)解析手機(jī) /system/usr/keylayout/gpio-key.kl獲取,或者根據(jù)需求直接寫(xiě)死
convert key 轉(zhuǎn)換成的按鍵:
這個(gè)在HiJackService初始化數(shù)據(jù)庫(kù)的時(shí)候,初始化每個(gè)按鍵的初始值。選擇不同的按鍵轉(zhuǎn)換后,會(huì)保存映射。
點(diǎn)擊具體的item轉(zhuǎn)換:

第一部分的list:
【1】通過(guò)讀取 frameworks/base/core/res/res/values/config.xml 中自定義的config_hijackingKeys數(shù)組,得到需要被定制的key:
<string-array name="config_hijackingKeys">
<item>"NoAction:KEYCODE_UNKNOWN:0:::0"</item>
<item>"HomeKey:KEYCODE_HOME:3:::0"</item>
<item>"VolumeUp:KEYCODE_VOLUME_UP:24:::0"</item>
<item>"F13:KEYCODE_F13:900:::0"</item>
............
</string-array>
其中的:::是分隔符
第二部分:
額外添加三個(gè)功能:
RunApplication?? 點(diǎn)擊按鍵運(yùn)行某個(gè)app
//獲取所有應(yīng)用package
mApplist = mContext.getPackageManager().queryIntentActivities(intent, 0);
//運(yùn)行選擇的應(yīng)用
Intent intent = new Intent(Intent.ACTION_RUN);
intent.setComponent(new ComponentName(data.getPackageNameOfExecuteApp(),
data.getActivityNameOfExecuteApp()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
mContext.startActivity(intent);
Broadcastkey,CustomIntent也是同樣原理。
最后在總結(jié)一下流程:
啟動(dòng)流程:
reboot->systemSever->HiJackService->? init database? and restore key Map->? TransformButtons APP? -> choose convet key? -> apply
特殊按鍵流程:
press key -> PhoneWindowManager ?interceptKeyBeforeQueueing and?interceptKeyBeforeDispatching ->?HiJackService.hijackingKey()? -> KeyEvent.hijackingKeyCode() // 特殊按鍵轉(zhuǎn)換例如homekey? backkey
普通按鍵:
inputDispatcher -> WindowInputEventReceiver -> WindowManagerService.dispatchInputEvent-> ViewRootImpl-> ViewRootImpl.enqueueInputEvent-> ViewRootImpl.doProcessInputEvents ->>ViewPreImeInputStage.processKeyEvent ->hijackingKey -> View dispatchKeyEvent-> KeyEvent.java dispatch() ? //普通按鍵轉(zhuǎn)換,例如自定義的F13
對(duì)了, 上傳了APP的源碼以供參考
http://download.csdn.net/download/zghlezh/10134989