您好,登錄后才能下訂單哦!
首先聲明我是做系統(tǒng)開發(fā)的(高通平臺),所以下面介紹的方法并不適合應(yīng)用開發(fā)者。
最經(jīng)有個需求要屏蔽HOME按鍵返回桌面并且實現(xiàn)自己的功能,發(fā)現(xiàn)以前的方式報錯用不了,上網(wǎng)搜索了一下,發(fā)現(xiàn)都是抄來抄去基本是無用的。網(wǎng)上的方法不外乎這幾種:
第一, 大家最常用的重寫onAttachedToWindow()方法,然后在HOME點擊事件KeyEvent.KEYCODE_HOME中做自己想做的事情,但是這個方法google處于安全考慮在android2.3.3之后就不支持了。
第二, 抓取系統(tǒng)log日志,判斷有沒有打印“android.intent.category.HOME”信息來獲得是否按下了HOME按鍵, 這樣做就算代碼知道是按下HOME鍵,那怎么阻止返回桌面呢? 這只能是截獲HOME按鍵,并不能屏蔽它返回桌面的功能。
第三, 修改framework源碼,在PhoneWindowManager中處理HOME按鍵的地方發(fā)送一個消息,然后在上層應(yīng)用中捕獲這個消息,這和上面是一樣的,只能截獲HOME按鍵,并不能阻止返回桌面的動作。
第四, 在setContentView之前getWindow().setFlags(FLAG_HOMEKEY_DISPATCHED, FLAG_HOMEKEY_DISPATCHED); 這個FLAG_HOMEKEY_DISPATCHED其實是個標志位,是個常量,值為0x80000000, 這個方法確實可以,但僅限于MTK(聯(lián)發(fā)科)平臺的系統(tǒng),因為MTK自己實現(xiàn)了一套機制來規(guī)避HOME按鍵,而其它的平臺,如高通、博通、展迅的代碼中是沒有加這個屬性的,所以也不行。
還有極少數(shù)思路很極端的方式,看上去都覺得很繁瑣,根本沒耐心細看。
現(xiàn)在介紹我的思路,首先還是復(fù)寫onAttachedToWindow()方法,具體代碼是在activity中加入這一段:
@Override
public void onAttachedToWindow() {
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD);
super.onAttachedToWindow();
}
結(jié)果當然是一進入這個activity就報錯,logcat查看錯誤信息,報的錯誤是"Window type can not be changed after the window is added.",發(fā)現(xiàn)是WindowManagerService.java中報的錯,源碼位置frameworks/base/services/java/com/android/server/wm/WindowManagerService.java,具體代碼段是relayoutWindow方法中判斷窗口類型的時候報錯:
if (attrs != null) {
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
flagChanges = win.mAttrs.flags ^= attrs.flags;
attrChanges = win.mAttrs.copyFrom(attrs);
if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
| WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
win.mLayoutNeeded = true;
}
}
剛才說了,google可能是出于安全原因不能讓你把窗口類型設(shè)為WindowManager.LayoutParams.TYPE_KEYGUARD了, 設(shè)置了就報錯,要屏蔽這個錯誤只需要把if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
}
注釋掉就行了,再次運行就不會報錯了,但是按HOME鍵還是返回桌面。繼續(xù)看分發(fā)HOME按鍵事件的代碼,源碼位置frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中的if (keyCode == KeyEvent.KEYCODE_HOME) {...}中,這里就是處理上層用戶按下HOME鍵的代碼,在里面會看到// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
// the "app" is keyguard, so give it the key
return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for (int i=0; i<typeCount; i++) {
if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
// don't do anything, but also don't pass it to the app
return -1;
}
}
}
這一段代碼,意思就是如果設(shè)置了窗口類型為 WindowManager.LayoutParams.TYPE_KEYGUARD(值為2004) 或者WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,那就return 0,交給用戶自己處理,不返回桌面,否則返回-1,返回桌面。通過打log發(fā)現(xiàn)代碼確實是return 0,但還是返回桌面,后來才發(fā)現(xiàn)還有方法在返回桌面:/**
* A home key -> launch home action was detected. Take the appropriate action
* given the situation with the keyguard.
*/
void launchHomeFromHotKey() {
if (mKeyguardMediator != null && mKeyguardMediator.isShowingAndNotHidden()) {
// don't launch home if keyguard showing
} else if (!mHideLockScreen && mKeyguardMediator.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardMediator.verifyUnlock(new OnKeyguardExitResult() {
public void onKeyguardExitResult(boolean success) {
if (success) {
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome();
}
}
});
} else {
// no keyguard stuff to worry about, just launch home!
try {
ActivityManagerNative.getDefault().stopAppSwitches();
} catch (RemoteException e) {
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome();
}
}
不知道高通怎么改的,就算return 0也會執(zhí)行這個方法,也會返回桌面,因為這個方法在判斷窗口類型前面調(diào)用,現(xiàn)在要做的就簡單了,只需要加個判斷,就可以屏蔽返回HOME了:
boolean isGoHome = true;
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
isGoHome = false;
}
}
// Go home!
if (isGoHome) {
launchHomeFromHotKey();
}
整個HOME按鍵的代碼段:
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
// timeout.
if (keyCode == KeyEvent.KEYCODE_HOME) {
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
cancelPreloadRecentApps();
mHomePressed = false;
if (mHomeConsumed) {
mHomeConsumed = false;
return -1;
}
if (canceled) {
Log.i(TAG, "Ignoring HOME; event canceled.");
return -1;
}
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallScreen at this point,
// and his ONLY options are to answer or reject the call.)
try {
ITelephony telephonyService = getTelephonyService();
if (telephonyService != null && telephonyService.isRinging()) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
return -1;
}
} catch (RemoteException ex) {
Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
}
// Delay handling home if a double-tap is possible.
if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
ViewConfiguration.getDoubleTapTimeout());
return -1;
}
boolean isGoHome = true;
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
isGoHome = false;
}
}
// Go home!
if (isGoHome) {
launchHomeFromHotKey();
}
return -1;
}
// If a system window has focus, then it doesn't make sense
// right now to interact with applications.
WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
if (attrs != null) {
final int type = attrs.type;
if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
// the "app" is keyguard, so give it the key
return 0;
}
final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
for (int i=0; i<typeCount; i++) {
if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
// don't do anything, but also don't pass it to the app
return -1;
}
}
}
// Remember that home is pressed and handle special actions.
if (repeatCount == 0) {
mHomePressed = true;
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
handleDoubleTapOnHome();
} else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
|| mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
preloadRecentApps();
}
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
if (!keyguardOn) {
handleLongPressOnHome();
}
}
return -1;
}
至此就可以屏蔽HOME按鍵了,在需在上層應(yīng)用中復(fù)寫onAttachedToWindow()方法即可,然后
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_HOME:
// 想實現(xiàn)的功能
break;
}
return true;
}
至此分析完畢,如果要屏蔽HOME鍵返回桌面不修改源碼是不行的,至少上面介紹的那些方法是不行的(MTK除外),所以有這個需求的童鞋找方法的時候就不要浪費時間去看那些抄來抄去的帖子了。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。