Android中外接键盘的检测的实现
今天来了一个问题:软键盘无法弹出。分析后是因为系统判断当前有外接硬键盘,就会隐藏软键盘。但实际情况并不是这么简单,该问题只有在特定条件下偶现,具体分析过程就不说了,就是软硬键盘支持上的逻辑问题。借着这个机会整理一下键盘检测的过程。
Configuration
Android系统中通过读取Configuration中keyboard的值来判断是否存在外接键盘。Configuration中关于键盘类型的定义如下,
publicstaticfinalintKEYBOARD_UNDEFINED=0;//未定义的键盘 publicstaticfinalintKEYBOARD_NOKEYS=1;//无键键盘,没有外接键盘时为该类型 publicstaticfinalintKEYBOARD_QWERTY=2;//标准外接键盘 publicstaticfinalintKEYBOARD_12KEY=3;//12键小键盘
在最常见的情况下,外接键盘未连接时keyboard的值为KEYBOARD_NOKEYS,当检测到键盘连接后会将keyboard的值更新为KEYBOARD_QWERTY。应用就可以根据keyboard的值来判断是否存在外接键盘,InputMethodService.java中有类似的判断代码。
//软件盘是否可以显示 publicbooleanonEvaluateInputViewShown(){ Configurationconfig=getResources().getConfiguration(); returnconfig.keyboard==Configuration.KEYBOARD_NOKEYS ||config.hardKeyboardHidden==Configuration.HARDKEYBOARDHIDDEN_YES; }
现在的问题就转向Configuration的keyboard是如何更新的。在WindowManagerService.java中,应用启动时会更新Configuration,相关代码如下。
booleancomputeScreenConfigurationLocked(Configurationconfig){ ...... if(config!=null){ //Updatetheconfigurationbasedonavailableinputdevices,lidswitch, //andplatformconfiguration. config.touchscreen=Configuration.TOUCHSCREEN_NOTOUCH; //默认值为KEYBOARD_NOKEYS config.keyboard=Configuration.KEYBOARD_NOKEYS; config.navigation=Configuration.NAVIGATION_NONAV; intkeyboardPresence=0; intnavigationPresence=0; finalInputDevice[]devices=mInputManager.getInputDevices(); finalintlen=devices.length; //遍历输入设备 for(inti=0;i影响Configuration中keyboard的值有,
- 默认值为KEYBOARD_NOKEYS,表示没有外接键盘。
- 当输入设备为KEYBOARD_TYPE_ALPHABETIC时,更新为KEYBOARD_QWERTY,一个标准键盘。
- 当Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD为1时,设置为KEYBOARD_NOKEYS,目的是让软键盘可以显示。
inputflinger
接下来需要关注输入设备时何时被设置KEYBOARD_TYPE_ALPHABETIC的。搜索代码可以看到,这个flag实在native代码中设置的,代码在inputflinger/InputReader.cpp中。native和java使用了同一定义值,如果修改定义时需要注意同时修改。native中的名字为AINPUT_KEYBOARD_TYPE_ALPHABETIC。
InputDevice*InputReader::createDeviceLocked(int32_tdeviceId,int32_tcontrollerNumber, constInputDeviceIdentifier&identifier,uint32_tclasses){ InputDevice*device=newInputDevice(&mContext,deviceId,bumpGenerationLocked(), controllerNumber,identifier,classes); ...... if(classes&INPUT_DEVICE_CLASS_ALPHAKEY){ keyboardType=AINPUT_KEYBOARD_TYPE_ALPHABETIC; } ...... returndevice; }InputReader在增加设备时,根据classes的flag来设置键盘类型。这个flag又是在EventHub.cpp中设置的。
status_tEventHub::openDeviceLocked(constchar*devicePath){ ...... //Configurethekeyboard,gamepadorvirtualkeyboard. if(device->classes&INPUT_DEVICE_CLASS_KEYBOARD){ //'Q'keysupport=cheaptestofwhetherthisisanalpha-capablekbd if(hasKeycodeLocked(device,AKEYCODE_Q)){ device->classes|=INPUT_DEVICE_CLASS_ALPHAKEY; } ...... }看到这里就比较明确了,在EventHub加载设备时,如果输入设备为键盘,并且带有'Q'键,就认为这是一个标准的外接键盘。但为何判断'Q'键还不是很清楚。
keylayout
上面说道通过'Q'键来判断是否为外接键盘,这个'Q'键是Android的键值,键值是否存在是通过一个keylayout文件决定的。kl文件存储在目标系统的/system/usr/keylayout/下,系统可以有多个kl文件,根据设备的ID来命名。当系统加载键盘设备时,就会根据设备的VendorID和ProductID在/system/usr/keylayout/下寻找kl文件。例如一个kl文件名为”Vendor_0c45_Product_1109.kl“,表明设备的VendorID为0c45,ProductID为1109。一个kl的内容示例如下,
key1BACK key28DPAD_CENTER key102HOME key103DPAD_UP key105DPAD_LEFT key106DPAD_RIGHT key108DPAD_DOWN key113VOLUME_MUTE key114VOLUME_DOWN key115VOLUME_UP key142POWER键值映射需要使用关键之”key“进行声明,之后跟着的数字为Linux驱动中的键值定义,再后面的字符串是Android中按键的名称。'Q'键是否存在完全取决于kl文件中是否有映射,而不是实际物理键是否存在。kl文件的查找也是有一个规则的,其查找顺序如下,
/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同时支持软硬键盘
有了上面的知识,就可以给出同时支持软硬键盘的方案。
- 修改源码逻辑,设置Configuration中keyboard的值为KEYBOARD_NOKEYS。这种Hack其实不好,破坏原生逻辑,缺乏移植性。非要这样改的话,可以增加对设备的判断,只有特定的键盘设备设置为KEYBOARD_NOKEYS,减少副作用。
- 修改keylayout,去掉'Q'键映射。有时kl文件写的不标准,为了通用把所有键的映射都写上了,实际硬件键却很少,我们就是这种情况。应该按照真实硬件来编写kl文件。
- 设置Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD为1。我认为这是最标准的修改方式,也非常方便。
关于第三个方案的修改方式有两种,一种是修改缺省的setting值,在文件frameworks/base/packages/SettingsProvider/res/values/defaults.xml中增加,
1 另一种方式是在系统启动时在代码中通过接口进行设置。
Settings.Secure.putInt(context.getContentResolver(),Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,1);以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。