最近工作中遇到在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()方法中
这样改软键盘是能用但是物理键盘是用不了的
最后研究代码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);
}
}
}
经过测试以上修改可以实现物理键盘与软键盘的同时启用,完美解决问题。
// 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.valueAt(i));
}
final int numDevices = mInputDevices.length;
deviceIdAndGeneration = new int[numDevices * 2];
for (int i = 0; i < numDevices; i++) {
final InputDevice inputDevice = mInputDevices[i];
deviceIdAndGeneration[i * 2] = inputDevice.getId();
deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration();
if (!inputDevice.isVirtual() && inputDevice.isFullKeyboard()) {
if (!containsInputDeviceWithDescriptor(oldInputDevices,
inputDevice.getDescriptor())) {
mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice);
} else {
mTempFullKeyboards.add(inputDevice);
}
}
}
}
// Notify listeners.
for (int i = 0; i < numListeners; i++) {
mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged(
deviceIdAndGeneration);
}
mTempInputDevicesChangedListenersToNotify.clear();
// Check for missing keyboard layouts.
if (mNotificationManager != null) {
final int numFullKeyboards = mTempFullKeyboards.size();
boolean missingLayoutForExternalKeyboard = false;
boolean missingLayoutForExternalKeyboardAdded = false;
boolean multipleMissingLayoutsForExternalKeyboardsAdded = false;
InputDevice keyboardMissingLayout = null;
synchronized (mDataStore) {
for (int i = 0; i < numFullKeyboards; i++) {
final InputDevice inputDevice = mTempFullKeyboards.get(i);
final String layout =
getCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier());
if (layout == null) {
missingLayoutForExternalKeyboard = true;
if (i < numFullKeyboardsAdded) {
missingLayoutForExternalKeyboardAdded = true;
if (keyboardMissingLayout == null) {
keyboardMissingLayout = inputDevice;
} else {
multipleMissingLayoutsForExternalKeyboardsAdded = true;
}
}
}
}
}
if (missingLayoutForExternalKeyboard) {
if (missingLayoutForExternalKeyboardAdded) {
if (multipleMissingLayoutsForExternalKeyboardsAdded) {
// We have more than one keyboard missing a layout, so drop the
// user at the generic input methods page so they can pick which
// one to set.
showMissingKeyboardLayoutNotification(null);
} else {
// 当前只插入一个物理键盘时,获取该设备名称判断是否是指定的物理键盘名,
// 如果是,则不显示键盘布局通知,否则显示通知
// Modify by Janning begin
if (keyboardMissingLayout != null
&& !keyboardMissingLayout.getName().equals("XXXX-vinput-keypad")) {
showMissingKeyboardLayoutNotification(keyboardMissingLayout);
}
// Modify by Janning end
}
}
} else if (mKeyboardLayoutNotificationShown) {
hideMissingKeyboardLayoutNotification();
}
}
mTempFullKeyboards.clear();
}