android5.0 物理鍵盤與軟鍵盤同時使用修改
最近工作中遇到在android 5.0 系統插入硬體盤物理裝置後,軟鍵盤無法彈出的問題,在網上查找了相關資料:
參考:http://blog.csdn.net/DrakeBlue/article/details/39049495
http://blog.csdn.net/hclydao/article/details/44240799
http://blog.csdn.net/jdsjlzx/article/details/39495319
物理鍵盤對映過程:
手機/system/usr/keylayout/*.kl :核心將keyCode對映成有含義的字串
KeycodeLabels.h : framework 將字串對映成keyEvent的keyCode
frameworks/.../res/values/attrs.xml
frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
關鍵程式碼:行6618 computeScreenConfigurationLocked()方法中
[java] view plaincopy
- boolean
- if (hardKeyboardAvailable != mHardKeyboardAvailable) {
- mHardKeyboardAvailable = hardKeyboardAvailable;
- mHardKeyboardEnabled = !hardKeyboardAvailable;
- mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
- }
- if (!mHardKeyboardEnabled) {
- config.keyboard = Configuration.KEYBOARD_NOKEYS;
- }
這樣改軟鍵盤是能用但是物理鍵盤是用不了的
最後研究程式碼frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
如果把updateShowImeWithHardKeyboard方法中的showImeWithHardKeyboard變數直接置為true,則可以實現軟鍵盤與物理鍵盤的同時使用,
但此舉修改影響範圍很大,不推薦。
public void updateShowImeWithHardKeyboard() {
//modified by Janning for enble the HardKeyboard start
final boolean showImeWithHardKeyboard = Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0,
mCurrentUserId) == 1;
//final boolean showImeWithHardKeyboard = true;
//modified by Janning for enble the HardKeyboard end
synchronized (mWindowMap) {
if (mShowImeWithHardKeyboard != showImeWithHardKeyboard) {
mShowImeWithHardKeyboard = showImeWithHardKeyboard;
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
}
後續繼續研究程式碼發現在WindowManagerService.java的computeScreenConfigurationLocked方法中有通過判斷當前物理鍵盤型別來控制是否同時啟用軟體盤的處理邏輯:
boolean computeScreenConfigurationLocked ( Configuration config ) { if (! mDisplayReady ) { return false ; } // TODO(multidisplay): For now, apply Configuration to main screen only. final DisplayContent displayContent = getDefaultDisplayContentLocked (); // Use the effective "visual" dimensions based on current rotation final boolean rotated = ( mRotation == Surface . ROTATION_90 || mRotation == Surface . ROTATION_270 ); final int realdw = rotated ? displayContent . mBaseDisplayHeight : displayContent . mBaseDisplayWidth ; final int realdh = rotated ? displayContent . mBaseDisplayWidth : displayContent . mBaseDisplayHeight ; int dw = realdw ; int dh = realdh ; if ( mAltOrientation ) { if ( realdw > realdh ) { // Turn landscape into portrait. int maxw = ( int )( realdh / 1.3f ); if ( maxw < realdw ) { dw = maxw ; } } else { // Turn portrait into landscape. int maxh = ( int )( realdw / 1.3f ); if ( maxh < realdh ) { dh = maxh ; } } } if ( config != null ) { config . orientation = ( dw <= dh ) ? Configuration . ORIENTATION_PORTRAIT : Configuration . ORIENTATION_LANDSCAPE ; } // Update application display metrics. final int appWidth = mPolicy . getNonDecorDisplayWidth ( dw , dh , mRotation ); final int appHeight = mPolicy . getNonDecorDisplayHeight ( dw , dh , mRotation ); final DisplayInfo displayInfo = displayContent . getDisplayInfo (); synchronized ( displayContent . mDisplaySizeLock ) { displayInfo . rotation = mRotation ; displayInfo . logicalWidth = dw ; displayInfo . logicalHeight = dh ; displayInfo . logicalDensityDpi = displayContent . mBaseDisplayDensity ; displayInfo . appWidth = appWidth ; displayInfo . appHeight = appHeight ; displayInfo . getLogicalMetrics ( mRealDisplayMetrics , CompatibilityInfo . DEFAULT_COMPATIBILITY_INFO , null ); displayInfo . getAppMetrics ( mDisplayMetrics ); mDisplayManagerInternal . setDisplayInfoOverrideFromWindowManager ( displayContent . getDisplayId (), displayInfo ); } if ( false ) { Slog . i ( TAG , "Set app display size: " + appWidth + " x " + appHeight ); } final DisplayMetrics dm = mDisplayMetrics ; mCompatibleScreenScale = CompatibilityInfo . computeCompatibleScaling ( dm , mCompatDisplayMetrics ); if ( config != null ) { config . screenWidthDp = ( int )( mPolicy . getConfigDisplayWidth ( dw , dh , mRotation ) / dm . density ); config . screenHeightDp = ( int )( mPolicy . getConfigDisplayHeight ( dw , dh , mRotation ) / dm . density ); computeSizeRangesAndScreenLayout ( displayInfo , rotated , dw , dh , dm . density , config ); config . compatScreenWidthDp = ( int )( config . screenWidthDp / mCompatibleScreenScale ); config . compatScreenHeightDp = ( int )( config . screenHeightDp / mCompatibleScreenScale ); config . compatSmallestScreenWidthDp = computeCompatSmallestWidth ( rotated , dm , dw , dh ); config . densityDpi = displayContent . mBaseDisplayDensity ; // Update the configuration based on available input devices, lid switch, // and platform configuration. config . touchscreen = Configuration . TOUCHSCREEN_NOTOUCH ; config . keyboard = Configuration . KEYBOARD_NOKEYS ; config . navigation = Configuration . NAVIGATION_NONAV ; int keyboardPresence = 0 ; int navigationPresence = 0 ; final InputDevice [] devices = mInputManager . getInputDevices (); final int len = devices . length ; for ( int i = 0 ; i < len ; i ++) { InputDevice device = devices [ i ]; if (! device . isVirtual ()) { final int sources = device . getSources (); final int presenceFlag = device . isExternal () ? WindowManagerPolicy . PRESENCE_EXTERNAL : WindowManagerPolicy . PRESENCE_INTERNAL ; if ( mIsTouchDevice ) { if (( sources & InputDevice . SOURCE_TOUCHSCREEN ) == InputDevice . SOURCE_TOUCHSCREEN ) { config . touchscreen = Configuration . TOUCHSCREEN_FINGER ; } } else { config . touchscreen = Configuration . TOUCHSCREEN_NOTOUCH ; } if (( sources & InputDevice . SOURCE_TRACKBALL ) == InputDevice . SOURCE_TRACKBALL ) { config . navigation = Configuration . NAVIGATION_TRACKBALL ; navigationPresence |= presenceFlag ; } else if (( sources & InputDevice . SOURCE_DPAD ) == InputDevice . SOURCE_DPAD && config . navigation == Configuration . NAVIGATION_NONAV ) { config . navigation = Configuration . NAVIGATION_DPAD ; navigationPresence |= presenceFlag ; } // 判斷該物理裝置的型別, InputDevice.KEYBOARD_TYPE_ALPHABETIC 是表示物理鍵盤裝置 if ( device . getKeyboardType () == InputDevice . KEYBOARD_TYPE_ALPHABETIC ) { config . keyboard = Configuration . KEYBOARD_QWERTY ; keyboardPresence |= presenceFlag ; } // 獲取物理裝置名稱,判斷是否是指定的名稱,如果是則把 config.keyboard // 的屬性置為 Configuration.KEYBOARD_NOKEYS ,如此則可以同時相容軟鍵盤 // 物理鍵盤與軟鍵盤可以同時啟用 // Add by Janning start // for show IME with HardKeyboard if ( device . getName (). equals ( "XXX-vinput-keypad" )) { Slog . w ( "SLCODE" , "the hard device name is: " + device . getName ()); config . keyboard = Configuration . KEYBOARD_NOKEYS ; } // Add by Janning end } } if ( config . navigation == Configuration . NAVIGATION_NONAV && mHasPermanentDpad ) { config . navigation = Configuration . NAVIGATION_DPAD ; navigationPresence |= WindowManagerPolicy . PRESENCE_INTERNAL ; } // Determine whether a hard keyboard is available and enabled. boolean hardKeyboardAvailable = config . keyboard != Configuration . KEYBOARD_NOKEYS ; if ( hardKeyboardAvailable != mHardKeyboardAvailable ) { mHardKeyboardAvailable = hardKeyboardAvailable ; mH . removeMessages ( H . REPORT_HARD_KEYBOARD_STATUS_CHANGE ); mH . sendEmptyMessage ( H . REPORT_HARD_KEYBOARD_STATUS_CHANGE ); } if ( mShowImeWithHardKeyboard ) { config . keyboard = Configuration . KEYBOARD_NOKEYS ; } // Let the policy update hidden states. config . keyboardHidden = Configuration . KEYBOARDHIDDEN_NO ; config . hardKeyboardHidden = Configuration . HARDKEYBOARDHIDDEN_NO ; config . navigationHidden = Configuration . NAVIGATIONHIDDEN_NO ; mPolicy . adjustConfigurationLw ( config , keyboardPresence , navigationPresence ); } return true ; } public boolean isHardKeyboardAvailable () { synchronized ( mWindowMap ) { return mHardKeyboardAvailable ; } } public void updateShowImeWithHardKeyboard () { // 此處修改也可以實現物理鍵盤與軟鍵盤的同時啟用,即把showImeWithHardKeyboard 直接置為 true, // 但此方法影響太大,不推薦該方案,建議根據裝置名稱判斷 修改config.keyboard 屬性值(程式碼見上文) //changed by Janning start //modified by Janning for enble the HardKeyboard start final boolean showImeWithHardKeyboard = Settings . Secure . getIntForUser ( mContext . getContentResolver (), Settings . Secure . SHOW_IME_WITH_HARD_KEYBOARD , 0 , mCurrentUserId ) == 1 ; //final boolean showImeWithHardKeyboard = true; //modified by Janning for enble the HardKeyboard end //changed by Janning end synchronized ( mWindowMap ) { if ( mShowImeWithHardKeyboard != showImeWithHardKeyboard ) { mShowImeWithHardKeyboard = showImeWithHardKeyboard ; mH . sendEmptyMessage ( H . SEND_NEW_CONFIGURATION ); } } } |
經過測試以上修改可以實現物理鍵盤與軟鍵盤的同時啟用,完美解決問題。
二、插入物理鍵盤後通知欄不想彈出鍵盤佈局通知的問題修改
當插入物理鍵盤後通知欄會彈出相應的選擇鍵盤佈局通知,對於該通知可以選擇隱藏: 根據字串查詢到是在 frameworks\base\services\core\java\com\android\server\input\InputManagerService.java 中呼叫顯示該通知的, 進一步分析程式碼發現是在 deliverInputDevicesChanged 方法中控制通知的顯示。a> |
// Must be called on handler. private void deliverInputDevicesChanged ( InputDevice [] oldInputDevices ) { // Scan for changes. int numFullKeyboardsAdded = 0 ; mTempInputDevicesChangedListenersToNotify . clear (); mTempFullKeyboards . clear (); final int numListeners ; final int [] deviceIdAndGeneration ; synchronized ( mInputDevicesLock ) { if (! mInputDevicesChangedPending ) { return ; } mInputDevicesChangedPending = false ; numListeners = mInputDevicesChangedListeners . size (); for ( int i = 0 ; i < numListeners ; i ++) { mTempInputDevicesChangedListenersToNotify . add ( mInputDevicesChangedListeners |