Today I have a problem: the soft keyboard cannot pop up. After analysis, the system determines that there is an external hard keyboard, and the soft keyboard will be hidden. But the actual situation is not that simple. This problem can only occur occasionally under specific conditions. I won’t talk about the specific analysis process, it is just a logical problem in the support of software and hardware keyboards. Take this opportunity to sort out the keyboard detection process.
Configuration
In the Android system, you can determine whether there is an external keyboard by reading the value of the keyboard in Configuration. The definition of keyboard type in Configuration is as follows:
public static final int KEYBOARD_UNDEFINED = 0; // Undefined keyboard public static final int KEYBOARD_NOKEYS = 1; // Keyless keyboard, this type is not available when there is no external keyboard public static final int KEYBOARD_QWERTY = 2; // Standard external keyboard public static final int KEYBOARD_12KEY = 3; // 12Keyboard
In the most common case, the value of the keyboard when the external keyboard is not connected is KEYBOARD_NOKEYS. When the keyboard connection is detected, the value of the keyboard will be updated to KEYBOARD_QWERTY. The application can determine whether there is an external keyboard based on the value of the keyboard, and there are similar judgment codes in it.
// Is the software disk displayable public boolean onEvaluateInputViewShown() { Configuration config = getResources().getConfiguration(); return == Configuration.KEYBOARD_NOKEYS || == Configuration.HARDKEYBOARDHIDDEN_YES; }
Now the question turns to how Configuration's keyboard is updated. In, the Configuration will be updated when the application starts, and the relevant code is as follows.
boolean computeScreenConfigurationLocked(Configuration config) { ...... if (config != null) { // Update the configuration based on available input devices, lid switch, // and platform configuration. = Configuration.TOUCHSCREEN_NOTOUCH; // The default value is KEYBOARD_NOKEYS = Configuration.KEYBOARD_NOKEYS; = Configuration.NAVIGATION_NONAV; int keyboardPresence = 0; int navigationPresence = 0; final InputDevice[] devices = (); final int len = ; // traverse the input device for (int i = 0; i < len; i++) { InputDevice device = devices[i]; // If it is not a virtual input device, the Configuration will be updated based on the flags of the input device if (!()) { ...... // If the keyboard type of the input device is KEYBOARD_TYPE_ALPHABETIC, set the keyboard to KEYBOARD_QWERTY if (() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) { = Configuration.KEYBOARD_QWERTY; keyboardPresence |= presenceFlag; } } } ...... // Determine whether a hard keyboard is available and enabled. boolean hardKeyboardAvailable = != Configuration.KEYBOARD_NOKEYS; // Update hardware keyboard status if (hardKeyboardAvailable != mHardKeyboardAvailable) { mHardKeyboardAvailable = hardKeyboardAvailable; (H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); (H.REPORT_HARD_KEYBOARD_STATUS_CHANGE); } // If SHOW_IME_WITH_HARD_KEYBOARD is set in Setting, set the keyboard to KEYBOARD_NOKEYS, so that the software disk can display it if (mShowImeWithHardKeyboard) { = Configuration.KEYBOARD_NOKEYS; } ...... }
The values that affect the keyboard in Configuration are:
- The default value is KEYBOARD_NOKEYS, which means there is no external keyboard.
- When the input device is KEYBOARD_TYPE_ALPHABETIC, it is updated to KEYBOARD_QWERTY, a standard keyboard.
- When .SHOW_IME_WITH_HARD_KEYBOARD is 1, set to KEYBOARD_NOKEYS, the purpose is to allow the soft keyboard to display.
inputflinger
Next, you need to pay attention to when the input device is set to KEYBOARD_TYPE_ALPHABETIC. Searching the code, you can see that this flag is set in the native code, and the code is in inputflinger/. native and java use the same definition value. If you modify the definition, you need to pay attention to modifying it at the same time. The name in native is AINPUT_KEYBOARD_TYPE_ALPHABETIC.
InputDevice* InputReader::createDeviceLocked(int32_t deviceId, int32_t controllerNumber, const InputDeviceIdentifier& identifier, uint32_t classes) { InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), controllerNumber, identifier, classes); ...... if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; } ...... return device; }
When adding devices, InputReader sets the keyboard type according to the flag of the classes. This flag is set in again.
status_t EventHub::openDeviceLocked(const char *devicePath) { ...... // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycodeLocked(device, AKEYCODE_Q)) { device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } ...... }
It is clearer to see this. When EventHub loads a device, if the input device is a keyboard and has the 'Q' key, it is considered to be a standard external keyboard. But why it is not clear to judge the 'Q' key.
keylayout
The above said that it is determined whether it is an external keyboard through the 'Q' key. This 'Q' key is the Android key value, and whether the key value exists is determined by a keylayout file. The kl file is stored in /system/usr/keylayout/ of the target system. The system can have multiple kl files, named according to the device ID. When the system loads the keyboard device, the kl file will be searched for /system/usr/keylayout/ based on the device's Vendor ID and Product ID. For example, a kl file named "Vendor_0c45_Product_1109.kl", indicating that the device's Vendor ID is 0c45 and Product ID is 1109. An example of the content of a kl is as follows:
key 1 BACK key 28 DPAD_CENTER key 102 HOME key 103 DPAD_UP key 105 DPAD_LEFT key 106 DPAD_RIGHT key 108 DPAD_DOWN key 113 VOLUME_MUTE key 114 VOLUME_DOWN key 115 VOLUME_UP key 142 POWER
The key-value mapping needs to be declared using the key "key", followed by the number is defined as the key value in the Linux driver, and the string followed by is the name of the key in Android. Whether the 'Q' key exists entirely depends on whether there is a mapping in the kl file, not whether the actual physical key exists. There is also a rule for searching kl files, and the search order is as follows.
/system/usr/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl /system/usr/keylayout/Vendor_XXXX_Product_XXXX.kl /system/usr/keylayout/DEVICE_NAME.kl /data/system/devices/keylayout/Vendor_XXXX_Product_XXXX_Version_XXXX.kl /data/system/devices/keylayout/Vendor_XXXX_Product_XXXX.kl /data/system/devices/keylayout/DEVICE_NAME.kl /system/usr/keylayout/ /data/system/devices/keylayout/
Supports hardware and software keyboards
With the above knowledge, you can give a solution that supports both software and hardware keyboards.
- Modify the source code logic and set the value of keyboard in Configuration to KEYBOARD_NOKEYS. This kind of hack is actually not good, destroying native logic and lacking portability. If you have to change this way, you can increase your judgment on the device. Only a specific keyboard device is set to KEYBOARD_NOKEYS to reduce side effects.
- Modify keylayout and remove the 'Q' key mapping. Sometimes the kl file is written in a non-standard way. In order to generalize, the mapping of all keys is written on, but there are very few actual hardware keys. This is the case. KL files should be written according to real hardware.
- Set .SHOW_IME_WITH_HARD_KEYBOARD to 1. I think this is the most standard modification method and it is also very convenient.
There are two ways to modify the third solution. One is to modify the default setting value and add it to the file frameworks/base/packages/SettingsProvider/res/values/.
<integer name="def_show_ime_with_hard_keyboard">1</integer>
Another way is to set it through the interface in the code when the system is started.
((), .SHOW_IME_WITH_HARD_KEYBOARD, 1);
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.