Realization of External Keyboard Detection in Android

  • 2021-11-13 18:19:39
  • OfStack

Today, there is a problem: the soft keyboard cannot pop up. After analysis, because the system judges that there is an external hard keyboard at present, it will hide the soft keyboard. However, the actual situation is not so simple. This problem only happens occasionally under specific conditions, and the specific analysis process will not be mentioned, that is, the logic problem on the support of soft and hard keyboards. Take this opportunity to sort out the process of keyboard detection.

Configuration

In Android system, the value of keyboard in Configuration is read to judge whether there is an external keyboard. 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 when there is no external keyboard 
  public static final int KEYBOARD_QWERTY = 2; //  Standard external keyboard 
  public static final int KEYBOARD_12KEY = 3; // 12 Key keypad 

In the most common case, the value of keyboard is KEYBOARD_NOKEYS when the external keyboard is not connected, and the value of keyboard is updated to KEYBOARD_QWERTY when a keyboard connection is detected. The application can judge whether there is an external keyboard according to the value of keyboard, and there are similar judgment codes in InputMethodService. java.


  //  Can the software disk be displayed 
  public boolean onEvaluateInputViewShown() {
    Configuration config = getResources().getConfiguration();
    return config.keyboard == Configuration.KEYBOARD_NOKEYS
        || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
  }  

Now the question turns to how keyboard of Configuration is updated. In WindowManagerService. java, Configuration is 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.
      config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
      //  The default value is KEYBOARD_NOKEYS
      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;
      //  Traversing input device 
      for (int i = 0; i < len; i++) {
        InputDevice device = devices[i];
        //  If it is not a virtual input device, it will be based on the input device's flags To update Configuration
        if (!device.isVirtual()) {
          ......
          //  If the keyboard type of the input device is KEYBOARD_TYPE_ALPHABETIC Will the keyboard Set to KEYBOARD_QWERTY
          if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
            config.keyboard = Configuration.KEYBOARD_QWERTY;
            keyboardPresence |= presenceFlag;
          }
        }
      }
      ......
      // Determine whether a hard keyboard is available and enabled.
      boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
      //  Update hardware keyboard status 
      if (hardKeyboardAvailable != mHardKeyboardAvailable) {
        mHardKeyboardAvailable = hardKeyboardAvailable;
        mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
        mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
      }
      //  If Setting Medium SHOW_IME_WITH_HARD_KEYBOARD Is set to set the keyboard Set to KEYBOARD_NOKEYS So that the software disk can be displayed 
      if (mShowImeWithHardKeyboard) {
        config.keyboard = Configuration.KEYBOARD_NOKEYS;
      }
      ......
    }

The values that affect 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 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD is 1, it is set to KEYBOARD_NOKEYS for the soft keyboard to display.

inputflinger

Next, you need to pay attention to when KEYBOARD_TYPE_ALPHABETIC is set when inputting the device. Searching for the code shows that this flag is actually set in the native code, which is in inputflinger/InputReader. cpp. native and java use the same definition value of 1. If you modify the definition, you should pay attention to modify 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 InputReader adds devices, the keyboard type is set according to flag of classes. This flag is set in EventHub. cpp.


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 clear from this that when EventHub loads the device, if the input device is a keyboard with the 'Q' key, it is considered as a standard external keyboard. However, it is not clear why the 'Q' bond is judged.

keylayout

It is said above that whether it is an external keyboard is judged by the 'Q' key. This' Q 'key is the key value of Android, and whether the key value exists is determined by an keylayout file. The kl file is stored under the/system/usr/keylayout/of the target system, and the system can have multiple kl files named after the ID of the device. When the system loads the keyboard device, it looks for the kl file under/system/usr/keylayout/based on the device's Vendor ID and Product ID. For example, an kl file name is "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 an 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 for the key definition in the Linux driver, followed by the string for the name of the key in the Android. The existence of the 'Q' key depends entirely on whether there is a mapping in the kl file, not whether the actual physical key exists. kl file search is also a rule, its 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/Generic.kl

/data/system/devices/keylayout/Generic.kl

Both hard and soft keyboards are supported

With the above knowledge, you can give a solution that supports both soft and hard keyboards.

Modify the source logic and set the value of keyboard in Configuration to KEYBOARD_NOKEYS. This Hack is actually not good, which destroys native logic and lacks portability. If you have to change it like this, you can increase the judgment of the device, and only the specific keyboard device is set to KEYBOARD_NOKEYS to reduce the side effects. Modify keylayout to remove the 'Q' key mapping. Sometimes the kl file is not written in a standard, and all the key mappings are written in order to be universal, but the actual hardware keys are very few, which is the case with us. kl files should be written according to real hardware. Set Settings. Secure.SHOW_IME_WITH_HARD_KEYBOARD to 1. I think this is the most standard way to modify it, and it is also very convenient.

There are two ways to modify the third scheme. One is to modify the default setting value, which is added in the document frameworks/base/packages/SettingsProvider/res/values/defaults. xml.


<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 starts.


Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1);


Related articles: