热门内容

公众号"MAKE1"

获取行业最新资讯

请扫码添加

专业客服企业微信

android按键映射

简介

android按键映射

一: 用户空间的处理

1.处理的内容和流程

触摸屏和轨迹球上报的是坐标、按下、抬起等信息,信息量比较少。按键处理的过程稍微复杂,从驱动程序到Android的Java层受到的信息,键表示方式经过了两次转化,如图所示。

Android按键输入的两次转化

键扫描码Scancode是由Linux的Input驱动框架定义的整数类型。键扫描码Scancode经过一次转化后,形成按键的标签KeycodeLabel,是一个字符串的表示形式。按键的标签KeycodeLabel经过转换后,再次形成整数型的按键码keycode。在Android应用程序层,主要使用按键码keycode来区分。

 

在本地框架层libui的头文件中KeycodeLabels.h,按键码为整数值的格式,其定义KeyCode(枚举值)如下所示:

1. typedef enum KeyCode {

2. kKeyCodeUnknown = 0,

3. kKeyCodeSoftLeft = 1,

4. kKeyCodeSoftRight = 2,

5. kKeyCodeHome = 3,

6. kKeyCodeBack = 4,

7. // ...... 省略中间按键码

8. } KeyCode;

进而在定义了KeycodeLabels.h中定义了从字符串到整数的映射关系,数组KEYCODES,定义如下所示:

1. static const KeycodeLabel KEYCODES[] = { // {字符串,整数}

2. { "SOFT_LEFT", 1 },

3. { "SOFT_RIGHT", 2 },

4. { "HOME", 3 },

5. { "BACK", 4 },

6. { "CALL", 5 },

7. { "ENDCALL", 6 },

8. { "0", 7 }, // ...... 数字按键

9. { "1", 8 },

10. { "2", 9 },

11. { "3", 10 },

12. { "4", 11 },

13. { "5", 12 },

14. { "6", 13 },

15. { "7", 14 },

16. { "8", 15 },

17. { "9", 16 },

18. { "STAR", 17 },

19. // ...... 省略中间按键映射

20. { "MENU", 82 },

21. // ...... 省略中间按键映射

22. { NULL, 0 }

23. };

数组KEYCODES表示的映射关系,左列的内容即表示按键标签KeyCodeLabel,右列的内容为按键码KeyCode(与KeyCode的数值对应)。实际上,在按键信息第二次转化的时候就是将字符串类型KeyCodeLabel转化成整数的KeyCode。

KeycodeLabel的Flags的定义如下所示:

1. static const KeycodeLabel FLAGS[] = {

2. { "WAKE", 0x00000001 }, // 可以唤醒睡眠,并通知应用层

3. { "WAKE_DROPPED", 0x00000002 }, // 可以唤醒睡眠,不通知应用层

4. { "SHIFT", 0x00000004 }, // 自动附加SHIFT

5. { "CAPS_LOCK", 0x00000008 }, // 自动附加CAPS_LOCK

6. { "ALT", 0x00000010 }, // 自动附加ALT

7. { "ALT_GR", 0x00000020 },

8. { "MENU", 0x00000040 },

9. { "LAUNCHER", 0x00000080 },

10. { NULL, 0 }

11. };

KeycodeLabel表示按键的附属标识。

提示:frameworks/base/core/Java/android/view/KeyEvent.Java中定义了类android.view. KeyEvent类,其中定义整数类型的数值与KeycodeLabels.h中定义的KeyCode枚举值是对应的。

在本地框架层libui的头文件中KeyCharacterMap.h,定义了按键的字符映射关系,KeyCharacterMap类的定义如下所示:

1. class KeyCharacterMap

2. {

3. public:

4. ~KeyCharacterMap();

5. unsigned short get(int keycode, int meta);

6. unsigned short getNumber(int keycode);

7. unsigned short getMatch(int keycode, const unsigned short* chars,

8. int charsize, uint32_t modifiers);

9. unsigned short getDisplayLabel(int keycode);

10. bool getKeyData(int keycode, unsigned short *displayLabel,

11. unsigned short *number, unsigned short* results);

12. inline unsigned int getKeyboardType() { return m_type; }

13. bool getEvents(uint16_t* chars, size_t len,

14. Vector<int32_t>* keys, Vector<uint32_t>* modifiers);

15. static KeyCharacterMap* load(int id);

16. enum {

17. NUMERIC = 1,

18. Q14 = 2,

19. QWERTY = 3 // or AZERTY or whatever

20. };

21. }

KeyCharacterMap用于将按键的码映射为文本可识别的字符串(例如,显示的标签等)。KeyCharacterMap是一个辅助的功能:由于按键码只是一个与UI无关整数,通常用程序对其进行捕获处理,然而如果将按键事件转换为用户可见的内容,就需要经过这个层次的转换了。

KeyCharacterMap需要从本地层传送到Java层,JNI的代码路径如下所示:

frameworks/base/core/jni/android_text_KeyCharacterMap.cpp

KeyCharacterMap Java框架层次的代码如下所示:

frameworks/base/core/Java/android/view/KeyCharacterMap.Java

android.view.KeyCharacterMap类是Android平台的API可以在应用程序中使用这个类。

android.text.method中有各种Linstener,可以之间监听KeyCharacterMap相关的信息。DigitsKeyListener NumberKeyListener TextKeyListener。

以上关于按键码和按键字符映射的内容是在代码中实现的内容,还需要配合动态的配置文件来使用。在实现Android系统的时候,有可能需要更改这两种文件。

动态的配置文件包括:

  • KL(Keycode Layout):后缀名为kl的配置文件
  • KCM(KeyCharacterMap):后缀名为kcm的配置文件

Donut及其之前配置文件的路径为:

development/emulator/keymaps/

Eclair及其之后配置文件的路径为:

sdk/emulator/keymaps/

这些配置文件经过系统生成后,将被放置在目标文件系统的/system/usr/keylayout/目录或者/system/usr/keychars/目录中。

提示:kl文件将被直接复职到目标文件系统中;由于尺寸较大,kcm文件放置在目标文件系统中之前,需要经过压缩处理。KeyLayoutMap.cpp负责解析处理kl文件,KeyCharacterMap.cpp负责解析kcm文件。

2.kl:按键布局文件

Android默认提供的按键布局文件主要包括qwerty.kl和AVRCP.kl。qwerty.kl为全键盘的布局文件,是系统中主要按键使用的布局文件;AVRCP.kl用于多媒体的控制,ACRCP的含义为Audio/Video Remote Control Profile。

qwerty.kl文件的片断如下所示:

1. key 399 GRAVE

2. key 2 1

3. key 3 2

4. key 4 3

5. key 5 4

6. key 6 5

7. key 7 6

8. key 8 7

9. key 10 9

10. key 11 0

11. key 158 BACK WAKE_DROPPED

12. key 230 SOFT_RIGHT WAKE

13. key 60 SOFT_RIGHT WAKE

14. key 107 ENDCALL WAKE_DROPPED

15. key 62 ENDCALL WAKE_DROPPED

16. key 229 MENU WAKE_DROPPED

17. # 省略部分按键的对应内容

18. key 16 Q

19. key 17 W

20. key 18 E

21. key 19 R

22. key 20 T

23. key 115 VOLUME_UP

24. key 114 VOLUME_DOWN

在按键布局文件中,第1列为按键的扫描码,是一个整数值;第2列为按键的标签,是一个字符串。即完成了按键信息的第1次转化,将整型的扫描码,转换成字符串类型的按键标签。第3列表示按键的Flag,

SHIFT: 当按下,自动加上SHIFT键值

ALT:当按下,自动加上ALT

CAPS:当按下,自动带上CAPS大写

WAKE:当按下,当设备进入睡眠的时候,按下这个键将唤醒,而且发送消息给应用层。

WAKE_DROPPED:当按下,且设备正处于睡眠,设备被唤醒,但是不发送消息给应用层。

扫描码来自驱动程序,显然不同的扫描码可以对应一个按键标签。表示物理上的两个按键可以对应同一个功能按键。

例如,上面的扫描码为158的时候,对应的标签为 BACK ,再经过第二次转换,根据KeycodeLabels.h的KEYCODES数组,其对应的按键码为4。

提示:按键布局文件其实同时兼顾了input驱动程序的定义和Android中按键的定义。例如:input驱动程序中定义的数字扫描码KEY_1的数值为2,这里2对应的按键标签也为“1”;input驱动程序中定义字母扫描码KEY_Q的数值为16,这里对应的按键标签也为“Q”。然而移动电话的全键盘毕竟有所不同,因此有一些按键是和input驱动程序的定义没有对应关系的。

kl文件将以原始的文本文件的形式,放置于目标文件系统的/system/usr/keylayout/目录或者/system/usr/keychars/目录中。

3.kcm:按键字符映射文件

kcm表示按键字符的映射关系,主要功能是将整数类型按键码(keycode)转化成可以显示的字符。

qwerty.kcm表示全键盘的字符映射关系,其片断如下所示:

1. [type=QWERTY]

2. # keycode display number base caps fn caps_fn

3.

4. A 'A' '2' 'a' 'A' '#' 0x00

5.

6. B 'B' '2' 'b' 'B' '<' 0x00

7.

8. C 'C' '2' 'c' 'C' '9' 0x00E7

9.

10. D 'D' '3' 'd' 'D' '5' 0x00

11.

12. E 'E' '3' 'e' 'E' '2' 0x0301

13.

14. F 'F' '3' 'f' 'F' '6' 0x00A5

15.

16. G 'G' '4' 'g' 'G' '-' '_'

17.

18. H 'H' '4' 'h' 'H' '[' '{'

19.

20. I 'I' '4' 'i' 'I' '$' 0x0302

21.

22. J 'J' '5' 'j' 'J' ']' '}'

23.

24. K 'K' '5' 'k' 'K' '"' '~'

25.

26. L 'L' '5' 'l' 'L' ''' '`'

27.

28. M 'M' '6' 'm' 'M' '!' 0x00

29.

30. N 'N' '6' 'n' 'N' '>' 0x0303

第一列是转换之前的按键码,第二列之后分别表示转换成为的显示内容(display),数字(number)等内容。这些转化的内容和KeyCharacterMap.h中定义的getDisplayLabel(),getNumber()等函数相对应。

这里的类型,除了QWERTY之外,还可以是Q14(单键多字符对应的键盘),NUMERIC(12键的数字键盘)。

kcm文件将被makekcharmap工具转化成二进制的格式,放在目标系统的/system/usr/keychars/目录中。

二: 移植需要注意的情况

1.EventHub中基本的处理

libui库中frameworks/base/libs/ui中的EventHub.cpp文件是用户输入系统的中枢,主要的功能都是在这个文件中实现的。

EventHub.cpp中定义设备节点所在的路径,内容如下所示:

1. static const char *device_path = "/dev/input"; // 输入设备的目录

在处理过程中,将搜索路径下面的所有Input驱动的设备节点,这在openPlatformInput()中通过调用scan_dir()来实现,scan_dir()将会从目录中查找设备,找到后调用open_device()将其打开。

1. bool EventHub::openPlatformInput(void)

2.

3. {

4.

5. // ...... 省略其他部分的内容

6.

7. res = scan_dir(device_path);

8.

9. return true;

10.

11. }

EventHub的getEvent()函数负责处理中完成,处理过程是在一个无限循环之内,调用阻塞的函数等待事件到来。

1. bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,

2.

3. int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,

4.

5. int32_t* outValue, nsecs_t* outWhen)

6.

7. {

8.

9. while(1) {

10.

11. // ...... 省略部分内容

12.

13. pollres = poll(mFDs, mFDCount, -1); // 使用poll处理设备节点,进行阻塞

14.

15. // ...... 省略部分内容

16.

17. for(i = 1; i < mFDCount; i++) {

18.

19. if(mFDs[i].revents) {

20.

21. if(mFDs[i].revents & POLLIN) {

22.

23. res = read(mFDs[i].fd, &iev, sizeof(iev)); // 读取信息

24.

25. // ...... 省略部分内容

26.

27. }

28.

29. }

30.

31. }

32.

33. }

poll()函数将会阻塞程序的运行,此时为等待状态,无开销,直到Input设备的相应事件发生,事件发生后poll()将返回,然后通过read()函数读取Input设备发生的事件代码。

注意,EventHub默认情况可以在/dev/input之中扫描各个设备进行处理,通常情况下所有的输入设备均在这个目录中。

实际上,系统中可能有一些input设备可能不需要被Android整个系统使用,也就是说不需要经过EventHub的处理,在这种情况下可以根据EventHub中open_device()函数的处理,设置驱动程序中的一些标志,屏蔽一些设备。open_device()中处理了键盘,轨迹球和触摸屏等几种设备,对其他设备可以略过。另外一个简单的方法就是将不需要EventHub处理的设备的设备节点不放置在/dev/input之中。

open_device()函数还将打开system/usr/keylayout/中的kl文件来处理,处理的过程如下所示:

1. int EventHub::open_device(const char *deviceName) {

2.

3. // ...... 省略部分内容

4.

5. const char* root = getenv("ANDROID_ROOT");

6.

7. snprintf(keylayoutFilename, sizeof(keylayoutFilename),

8.

9. "%s/usr/keylayout/%s.kl", root, tmpfn);

10.

11. bool defaultKeymap = false;

12.

13. if (access(keylayoutFilename, R_OK)) {

14.

15. snprintf(keylayoutFilename, sizeof(keylayoutFilename),

16.

17. "%s/usr/keylayout/%s", root, "qwerty.kl");

18.

19. defaultKeymap = true;

20.

21. }

22.

23. // ...... 省略部分内容

24.

25. }

由此可见,默认情况下使用的就是qwerty.kl,这里只是扫描各个后缀名为kl的文件,然后交由KeyLayoutMap去解析处理,KeyLayoutMap是一个内部使用的类。

2.按键的增加

Android已经定义了比较丰富、完整的标准按键。在一般情况下,不需要为Android系统增加按键,只需要根据kl配置按键即可。在系统中有比较奇特按键的时候,需要更改Android系统的框架层来更改按键。

增加按键需要更改的文件较多,主要的文件如下所示。

  • frameworks/base/include/ui/KeycodeLabels.h:中的KeyCode枚举数值和KeycodeLabel 类型Code数组(以NULL为结尾)
  • frameworks/base/core/Java/android/view/KeyEvent.Java:定义整数值,作为平台的API供Java应用程序使用
  • frameworks/base/core/res/res/values/attrs.xml:表示属性的资源文件,需要修改其中的name="keycode"的attr。

示例:

对keypad来说,涉及到的文件有以下几个:

1,/vendor/qcom/7k_ffa_keypad.kl

首先在此文件中,添加新的键值信息:Example: key 123 WLS flag

注意:新加的键值不要与已有的重复。

 

2. kernel/arch/arm/mach-msm/keypad_surf_ffa.c

在keypad对应的键位添加自定义的键码 如 :123

 

3. frameworks/base/include/ui/keycodeLabels.h

在数组keycodes 中添加 新定义的信息

{ "MUTE", 91 }, { "WLS" , 92 }

枚举类型 KeyCode 中添加

kKeyCodeMute = 91

kKeyCodeWLS = 92

 

4. frameworks/base/core/res/res/values/attrs.xml

<enum name="KEYCODE_MUTE" value="91"/>

<enum name="KEYCODE_WLS" value="92" />

 

5. frameworks/base/core/java/android/view/KeyEvent.java

public static final int KEYCODE_MUTE = 91;

public static final int KEYCODE_WLS = 92;

注意: private static final int LAST_KEYCODE = KEYCODE_MUTE;

应改为: private static final int LAST_KEYCODE = KEYCODE_WLS;

在函数 public final boolean isSystem() 中,同样需要添加:

case KEYCODE_SEARCH:

case KEYCODE_WLS:

 

通过以上的更改,新的键值就添加上去了,由于更改了 KeyEvent,影响到了API,

所以需要make update-api

如果对新键值进行处理,可以通过获取相应的keycode,对它进行处理;

对于按键事件的处理一般如下文件中

6. frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java

综上可知,我们可以根据需求定义自己的键值,并对键值所对应的事件信息进行合理化处理。

注:

7k_ffa_keypad.kl是专门给7k_ffa_keypad输入设备使用的keylayout文件,所以修改7k_ffa_keypad.kl文件只是给键盘输入增加键值。如果是其他设备的输入,如x_input,而且当没有x_iput.kl存在的时候,那么默认的kl文件就是qwerty.kl(android-root/development/emulator/keymaps/qwerty.kl)

这个很重要。也就是说需要实现明白添加的键值是否是7k_ffa_keypad的input event

框架层增加完成后,只需要更改kl文件,增加按键的映射关系即可。

提示:在系统需要增加按键的时候,一种简易的做法是使用Android中已经定义的“生僻”按键码作为这个新增按键的键码。使用这种方式Android的框架层不需要做任何改动。这种方式的潜在问题是当某些第三方的应用可能已经使用那些生僻按键时,将意外激发系统的这种新增的按键。

2
 条评论
相关内容推荐