Android O 系統(tǒng)自定義物理按鍵轉(zhuǎn)換

需求:定制物理按鍵可以轉(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,109評(píng)論 25 709
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程,因...
    小菜c閱讀 7,353評(píng)論 0 17
  • afinalAfinal是一個(gè)android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,879評(píng)論 2 45
  • 品牌:利動(dòng) 全名:乳果糖口服溶液 醫(yī)生開(kāi)這個(gè)藥的作用是對(duì)于便秘、長(zhǎng)期不排便的人,嬰兒大人都適用,促進(jìn)腸胃蠕動(dòng),不拉...
    七七檸檬姑娘閱讀 373評(píng)論 0 0
  • 封閉式提問(wèn) 你覺(jué)著這些記者的溝通水平怎么樣?也不怎么樣。他們?cè)谔釂?wèn),但提的問(wèn)題都可以用簡(jiǎn)單的一個(gè)詞來(lái)回答,很多時(shí)候...
    鵬之翼閱讀 1,096評(píng)論 0 2

友情鏈接更多精彩內(nèi)容