標籤:系統 enc 中斷連線 on() 3.2 connect dcl board strong
需求1: android裝置內建九鍵的小鍵盤,此時小鍵盤被識別為HW Keyboard,預設與軟鍵盤不能共存,需要使軟鍵盤與物理鍵盤共存。
實現:
在網上找的別人總結的Android5.1的解決方案,需要解決的codebase為Android6.0,都可以用。
方法一:(此方法在Android8.0 codebase已不可用) frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中,如果把updateShowImeWithHardKeyboard()方法中的showImeWithHardKeyboard變數直接置為true,則可以實現軟鍵盤與物理鍵盤的同時使用。(原本為讀取Setting資料庫欄位來判斷,所以也可以直接修改Setting欄位來實現)
public void updateShowImeWithHardKeyboard() { synchronized (mWindowMap) { final boolean showImeWithHardKeyboard = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mCurrentUserId) == 1;if (mShowImeWithHardKeyboard != showImeWithHardKeyboard) { mShowImeWithHardKeyboard = showImeWithHardKeyboard; mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } } }
方法二:frameworks/base/core/java/android/inputmethodservice/InputMethodService.java,修改onEvaluateInputViewShown()方法直接返回true
public boolean onEvaluateInputViewShown() { Configuration config = getResources().getConfiguration(); //return config.keyboard == Configuration.KEYBOARD_NOKEYS // || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; return true;}
方法三:在外部需要修改的合適位置,直接將Setting欄位修改掉,隨時可以改回來,一勞永逸。
Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1);// 1: Disabled,0: Enabled
需求2:軟鍵盤與內建物理鍵盤共存基礎上,再外接藍芽鍵盤。此時外接藍芽鍵盤與軟鍵盤不可共存。
分析:此時外接鍵盤也被視為物理鍵盤,按照上面需求1的方法修改後,使藍芽鍵盤與軟鍵盤是共存的,所以不符合需求。需要找到藍芽鍵盤串連上的位置,來把前面需求1改的地方取消掉。
即:未串連藍芽鍵盤時,將Setting欄位改為共存模式,使軟鍵盤與內建物理鍵盤共存;串連藍芽鍵盤後,將Setting欄位改為不共存模式,使軟鍵盤與藍芽鍵盤不共存(此時藍芽鍵盤與內建物理鍵盤是共存的)。
實現:本需求的痛點為找到恰當的位置來修改Setting欄位,此恰當位置為:藍芽鍵盤串連成功或斷開,以及藍芽關閉。
實現分析流程:
1. 通過Settings中Bluetooth Settings部分可以看到,串連藍芽鍵盤時,藍牙裝置的類型已經可以通過表徵圖區分出來。這樣就不用懷疑是否能夠區分藍芽鍵盤和其他藍牙裝置了。
在packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDevicePreference.java中根據icon可以看到,不同種類的BT device已經可以區分了。
private int getBtClassDrawable() { BluetoothClass btClass = mCachedDevice.getBtClass(); if (btClass != null) { switch (btClass.getMajorDeviceClass()) { case BluetoothClass.Device.Major.COMPUTER: return R.drawable.ic_bt_laptop; case BluetoothClass.Device.Major.PHONE: return R.drawable.ic_bt_cellphone; case BluetoothClass.Device.Major.PERIPHERAL: return HidProfile.getHidClassDrawable(btClass); case BluetoothClass.Device.Major.IMAGING: return R.drawable.ic_bt_imaging; default: // unrecognized device class; continue } } else { Log.w(TAG, "mBtClass is null"); }...
需要的藍芽鍵盤在 HidProfile.getHidClassDrawable(btClass)獲得。
frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
public static int getHidClassDrawable(BluetoothClass btClass) { switch (btClass.getDeviceClass()) { case BluetoothClass.Device.PERIPHERAL_KEYBOARD: case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING: return R.drawable.ic_lockscreen_ime; case BluetoothClass.Device.PERIPHERAL_POINTING: return R.drawable.ic_bt_pointing_hid; default: return R.drawable.ic_bt_misc_hid; } }
2. 通過抓系統藍芽profile log來確認中斷連線藍芽鍵盤時,會在framework裡面的Bluetooth的哪邊被trigger到。
adb logcat |grep Profile
在串連/斷開藍芽鍵盤時,可以抓到下面的log:
[email protected]:~$ adb logcat |grep Profile12-22 03:55:00.524 1257 1257 I SystemServer: SamplingProfiler Service12-22 04:29:41.923 3916 4051 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 012-22 06:25:59.666 7284 7284 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 212-22 06:25:59.668 3916 4051 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 212-22 06:26:33.245 7284 7284 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 012-22 06:26:33.245 3916 4051 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 012-22 06:26:45.586 7284 7284 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 212-22 06:26:45.587 3916 4051 D CachedBluetoothDevice: onProfileStateChanged: profile HID newProfileState 2...
可以定位到frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
在onProfileStateChanged方法中可以看到,藍芽串連和斷開時都可以走到!而且去獲得藍牙裝置類型的context也都有!這樣問題解決一大半!
在onProfileStateChanged方法中添加藍芽串連和斷開時的裝置類型判斷:
if(mBtClass.getMajorDeviceClass() == BluetoothClass.Device.Major.PERIPHERAL && (mBtClass.getDeviceClass() == BluetoothClass.Device.PERIPHERAL_KEYBOARD || mBtClass.getDeviceClass() == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING)) { Log.i("Kunkka0", "getMajorDeviceClass = Peripheral & getDeviceClass = KeyBoard"); if(newProfileState == 0) {// Disconnected Log.i("Kunkka0","SET SHOW_IME_WITH_HARD_KEYBOARD TO 1"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1); }else { Log.i("Kunkka0","SET SHOW_IME_WITH_HARD_KEYBOARD TO 0"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0); } }else { Log.i("Kunkka0","getMajorDeviceClass() = "+mBtClass.getMajorDeviceClass()+", getDeviceClass = "+mBtClass.getDeviceClass()); Log.i("Kunkka0","PERIPHERAL = 1280, PERIPHERAL_KEYBOARD = 1344, PERIPHERAL_KEYBOARD_POINTING = 1472"); }
加完編譯出image來測試,功能實現!
但是,在關閉藍芽時,藍芽鍵盤就不能用了,但是此處不能收到狀態變化!此時,關閉藍芽,軟鍵盤和藍芽鍵盤都不能用了!
3. 為瞭解決關閉藍芽時的問題,再去搜尋藍芽開關狀態變化的事件:
在frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java中可以看到有:
void onBluetoothStateChanged(int bluetoothState); void onScanningStateChanged(boolean started); void onDeviceAdded(CachedBluetoothDevice cachedDevice); void onDeviceDeleted(CachedBluetoothDevice cachedDevice); void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState); void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
查看所有重載此事件的class,Open implementation-可以看到:
下面幾個都是Settings中的,但我們的需求不止在Settings中,所以看上面KeyboardUI和systemui下面的statusbar。
StatusBar下面的BluetoothControllerImpl比較符合。
開啟frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
除了onBluetoothStateChanged外還有個updateConnected()方法,居然也可以偵測藍牙裝置串連狀態!
所以我們只需要在onBluetoothStateChanged()和updateConnected()中都加入事件偵測來修改Settings欄位就可以了!
4. 最終解決辦法:
a. 在onBluetoothStateChanged()中,當藍芽關閉時,使鍵盤共存,保證軟鍵盤和內建物理鍵盤的共存。
@Override public void onBluetoothStateChanged(int bluetoothState) { mEnabled = bluetoothState == BluetoothAdapter.STATE_ON; if(!mEnabled) { Log.i("Kunkka0","onBluetoothStateChanged: SET SHOW_IME_WITH_HARD_KEYBOARD TO 1"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1); } mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED); }
b. 在updateConnected()中,當藍芽鍵盤串連時,使軟硬鍵盤不共存,藍芽鍵盤斷開時,使軟硬體盤共存。
private void updateConnected() { ... for (CachedBluetoothDevice device : getDevices()) { if (device.isConnected()) { mLastDevice = device; } if(device.getBtClass().getMajorDeviceClass() == BluetoothClass.Device.Major.PERIPHERAL && (device.getBtClass().getDeviceClass() == BluetoothClass.Device.PERIPHERAL_KEYBOARD || device.getBtClass().getDeviceClass() == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING)) { Log.i("Kunkka0", "getMajorDeviceClass = Peripheral & getDeviceClass = KeyBoard"); if(!device.isConnected()) {// Disconnected Log.i("Kunkka0","SET SHOW_IME_WITH_HARD_KEYBOARD TO 1"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1); }else { Log.i("Kunkka0","SET SHOW_IME_WITH_HARD_KEYBOARD TO 0"); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0); } }else { Log.i("Kunkka0","getMajorDeviceClass() = "+device.getBtClass().getMajorDeviceClass()+", getDeviceClass = "+device.getBtClass().getDeviceClass()); Log.i("Kunkka0","PERIPHERAL = 1280, PERIPHERAL_KEYBOARD = 1344, PERIPHERAL_KEYBOARD_POINTING = 1472"); } } ... }
測試,驗證通過!
Android源碼開發筆記 軟鍵盤與內建物理鍵盤共存以及外接藍芽鍵盤不共存邏輯