按键映射

1 键扫描码Scancode是由Linux的Input驱动框架定义的整数类型。

include/uapi/linux/input.h中有定义:

......
#define KEY_VOLUMEDOWN 114
#define KEY_VOLUMEUP 115
#define KEY_POWER 116 /* SC System Power Down */
#define KEY_MENU 139 /* Menu (show menu) */
#define KEY_BACK 158 /* AC Back */
......


2 键扫描码Scancode经过一次转化后,形成按键的标签KeycodeLabel->literal,是一个字符串的表示形式。

2.1 KCM(KeyCharacterMap):按键字符映射文件,位于/system/usr/keychars/目录,由KeyCharacterMap.cpp解析。

按键字符映射文件片段:

type FULLkey A {
 label: 'A' base: 'a' shift, 
 capslock: 'A'
}
key SPACE {
 label: ' ' 
 base: ' ' alt,
 meta: fallback SEARCH 
 ctrl: fallback LANGUAGE_SWITCH
}

Keyboard Type Declaration

一个键盘的type声明描述了键盘的总体行为。一个字符映射文件必须包含一个键盘type声明。为清楚起见,它通常被放在文件顶部。
可识别的键盘type如下:
(1) NUMERIC: 一个数字键盘(12个按键)。
一个数字键盘支持多次单击形式的文本输入。这对重复点击一个按键来产生期望的字母或符合的情况可能是必需的。
该键盘type通常是为了拇指输入设计的。
(2) PREDICTIVE: 一个带有全字母的键盘,每个按键代表一个以上的字母。
该键盘type通常是为了拇指输入设计的。
(3) ALPHA: 一个带有全字母的键盘,也可能有一些数字。
一个字母键盘支持直接的文本编辑,但也可能有一个小型化的浓缩布局。与FULL键盘相反,一些符号只能使用屏幕上特殊的字符选择器进行编辑。此外,为了提高输入速度和精确度,framework为字符键盘提供了特殊的支持,例如自动大写和切换/锁定SHIFT和ALT按键。该键盘type通常是为了拇指输入设计的。
(4) FULL: 一个全PC样式的键盘。
一个完整的键盘像一个PC键盘。所有符号都可以用键盘上的按键直接访问,不再有屏幕支持的字符选择器,也不再提供类似自动大写的支持。
(5) SPECIAL_FUNCTION: 一个键盘只用于执行系统的控制函数,而不用于输入。
一个特殊功能键盘只包括没有输入功能的按键,比如HOME和POWER并不是用来做输入的。
The Generic.kcm and Virtual.kcm key character maps are both FULL keyboards.

Key Declarations

每个key声明包括一个关键字key,随后是一个android key code name,一个{,设置 properties and behaviors,最后一个}。

(A) Properties

每个key属性建立一个从key到behavior的映射。为了更紧密地创建按键字符映射文件,一些属性可以被映射到同一behavior,用逗号分隔。
可识别的属性如下:
(1) label: 指定按键上物理打印的标签,它由单一字符组成,转换成为的显示内容。
number: 指定数字文本视图获得焦点的行为(字符必须被输入),例如当用户输入一个电话号码。转换成为的显示数字。紧密结合的键盘常常在一个按键上合并多个符号,这样同一个按键可以被用作'1' and 'a' or '#' and 'q' 进行输入。可能,对于这些按键,如果需要的话,它们的数字属性被用来指明在数字上下文中输入的是哪个信号。
一些典型的“数字”信号是数字'0' through '9', '#', '+', '(', ')', ',', and '.'。
(2) base: 指定没有modifier 被按下的行为(字符必须被输入)。
<modifier> or <modifier1>+<modifier2>+...: 指定按键被按下和所以的指定modifier 被激活的行为(字符必须被输入)。
例如,modifier 属性shift指定了一个应用LEFT SHIFT or RIGHT SHIFT 修饰符被按下的行为。
同样地,modifier 属性rshift+ralt指定了应用RIGHT SHIFT and RIGHT ALT modifiers 一起被按下的行为。
(3) modifiers属性中可识别的modifiers如下:
shift: Applies when either the LEFT SHIFT or RIGHT SHIFT modifier is pressed.
lshift: Applies when the LEFT SHIFT modifier is pressed.
rshift: Applies when the RIGHT SHIFT modifier is pressed.
alt: Applies when either the LEFT ALT or RIGHT ALT modifier is pressed.
lalt: Applies when the LEFT ALT modifier is pressed.
ralt: Applies when the RIGHT ALT modifier is pressed.
ctrl: Applies when either the LEFT CONTROL or RIGHT CONTROL modifier is pressed.
lctrl: Applies when the LEFT CONTROL modifier is pressed.
rctrl: Applies when the RIGHT CONTROL modifier is pressed.
meta: Applies when either the LEFT META or RIGHT META modifier is pressed.
lmeta: Applies when the LEFT META modifier is pressed.
rmeta: Applies when the RIGHT META modifier is pressed.
sym: Applies when the SYMBOL modifier is pressed.
fn: Applies when the FUNCTION modifier is pressed.
capslock: Applies when the CAPS LOCK modifier is locked.
numlock: Applies when the NUM LOCK modifier is locked.
scrolllock: Applies when the SCROLL LOCK modifier is locked.
这些属性按顺序被列出是有意义的,当一个按键映射到一个行为,系统按顺序扫描所以相关的属性,并且返回最后一个发现的应用行为。
因此,给定一个按键,晚指定的属性会覆盖早指定的属性。

(B) Behaviors

每一个属性映射一种行为,大多数的行为都是输入一个字符,但是也有其他的。一些其他的可识别行为如下:(1) none: 不输入一个字符。
当没有字符指定的时候,这种行为是默认的。node是一个可选项,指定了none能提高清晰度。
(2) 'X': 输入一个指定的文字字符。
这种行为产生是由于一个指定的字符被输入到文本视图的焦点。这个文字字符可以是任何的ASCII字符或者是下列转义字符之一:
'\\': Type a backslash character.反斜杠。
'\n': Type a new line character (use this for ENTER / RETURN).换行。
'\t': Type a TAB character.
'\'': Type an apostrophe character.单引号。
'\"': Type a quote character.引号。
'\uXXXX': Type the Unicode character whose code point is given in hex by XXXX.unicode字符,XXXX'指向一个十六进制的数。
(3) fallback <Android key code name>: Perform a default action if the key is not handled by the application.如果按键没有被应用程序处理,就执行一个默认的动作。
该行为产生的原因是当应用程序本身不处理指定的按键时,系统模拟不同的按键按下。它是用来支持一个不是所有应用程序都知道怎么处理的新按键的默认行为,例如转义或者没有按下大小写锁定键的数字键盘按键。
当一个fallback行为被执行,应用程序将会接收到2个按键:一个是原始的按键,一个是所选的 fallback按键。如果应用程序在按键抬起的时候处理原始按键,那么fallback按键时间将被取消。
系统保留两个unicode编码执行特殊的函数:
'\uef00': 当该行为被执行,文本视图取消并移除光标前的四个字符,将该编码当做十六进制数字,插入到相应的unicode编码点。
'\uef01': 当该行为被执行,文本视图显示一个包含各种符号的字符选择对话框。
系统可以识别下列unicode编码作为dead key(与相关字符组合使用的修饰符键)按键字符的组合字符:
'\u0300': Grave accent.tab沉音符“`”,tab键上面的键。
'\u0301': Acute accent.尖音符。
'\u0302': Circumflex accent.长音符号。
'\u0303': Tilde accent.波浪线。
'\u0308': Umlaut accent.
当一个dead key接着另一个字符被输入,那么dead key和它跟随的字符是组合的。例如,当用户跟着字母'a'输入grave accent dead key,结构是'à'。

2.2 KL(Keycode Layout):按键布局文件,位于 /system/usr/keylayout/ 目录,由KeyLayoutMap.cpp解析。

按键布局文件片段如下:

key 158 BACK
key 139 MENU
key 108 DPAD_DOWN
key 103 DPAD_UP
key 102 HOME
key 115 VOLUME_UP WAKE
key 114 VOLUME_DOWN WAKE
key 116 POWER WAKE

第一列为按键的扫描码,是一个整数值;第2列为按键的标签,是一个字符串。第3列表示按键的Flag,经过*.kl即完成了按键信息的第1次转化,将整型的扫描码,转换成字符串类型的按键标签。
base/include/androidfw/KeycodeLabels.h中第3列flag定义:

static const KeycodeLabel FLAGS[] = { 
 { "WAKE", 0x00000001 }, //可以唤醒休眠,并通知应用层 
 { "WAKE_DROPPED", 0x00000002 }, //可以唤醒休眠,不通知应用层 
 { "SHIFT", 0x00000004 }, //自动附加SHIFT 
 { "CAPS_LOCK", 0x00000008 }, //自动附加CAPS_LOCK 
 { "ALT", 0x00000010 }, //自动附加ALT 
 { "ALT_GR", 0x00000020 }, 
 { "MENU", 0x00000040 }, 
 { "LAUNCHER", 0x00000080 }, 
 { "VIRTUAL", 0x00000100 }, 
 { "FUNCTION", 0x00000200 }, 
 { NULL, 0 }
};
 
 

2.3 转换按键码

native/include/android/keycodes.h中的枚举值定义了整数格式的按键码。

enum {
 AKEYCODE_UNKNOWN = 0,
 AKEYCODE_SOFT_LEFT = 1,
 AKEYCODE_SOFT_RIGHT = 2,  
 ...... AKEYCODE_ASSIST = 219, 
 AKEYCODE_BRIGHTNESS_DOWN = 220,
 AKEYCODE_BRIGHTNESS_UP = 221, 
// NOTE: If you add a new keycode here you must also add it to several other files. 
// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list.
};

base/include/androidfw/KeycodeLabels.h中KEYCODES[]数组定义了按键标签KeycodeLabel->literal和按键码KeycodeLabel->value的映射关系。

static const KeycodeLabel KEYCODES[] = { 
 { "SOFT_LEFT", 1 },  
 { "SOFT_RIGHT", 2 },  
 ......
 { NULL, 0 }
};

 
 

这些按键码与keycodes.h中枚举值的按键码是一致的。base/core/java/android/view/KeyEvent.java 中定义了android.view.KeyEvent类,其中定义的key code整型常量与按键码也是一致的。

public class KeyEvent extends InputEvent implements Parcelable 
{
/** Key code constant: Unknown key code. */
public static final int KEYCODE_UNKNOWN = 0; /** Key code constant: Soft Left key. * Usually situated below the display on phones and used as a multi-function * feature key for selecting a software defined function shown on the bottom left * of the display. */
public static final int KEYCODE_SOFT_LEFT = 1; /** Key code constant: Soft Right key. * Usually situated below the display on phones and used as a multi-function * feature key for selecting a software defined function shown on the bottom right * of the display. */  
public static final int KEYCODE_SOFT_RIGHT = 2;  
 ......
}
 
 
 
 

经过按键布局文件*.kl转换后,形成按键的标签KeycodeLabel->literal。根据KEYCODES[]的定义,通过这个标签可以找到对应的按键码KeycodeLabel->value。
按键的标签KeycodeLabel->literal经过转换后,再次形成整数型的按键码keycode。在Android应用程序层,主要使用按键码keycode来区分。

3 按键的添加

在系统需要增加按键的时候,一种简易的做法是使用Android中已经定义的“生僻”按键码作为这个新增按键的键码。使用这种方式Android的框架层不需要做任何改动。这种方式的潜在问题是当某些第三方的应用可能已经使用那些生僻按键时,将意外激发系统的这种新增的按键。
(1) include/uapi/linux/input.h中添加自定义的扫描码。

/* Code 255 is reserved for special needs of AT keyboard driver */
#define KEY_CUSTOM 249


(2) *.kl按键布局文件中添加键值信息,不要与已有信息重复。

key 249 CUSTOM

(3) native/include/android/keycodes.h中添加键值,这是给native空间用的。

enum {
 AKEYCODE_UNKNOWN = 0,
 AKEYCODE_SOFT_LEFT = 1, 
 AKEYCODE_SOFT_RIGHT = 2,  
 ......  
 AKEYCODE_CUSTOM = 222,
};

(4) base/include/androidfw/KeycodeLabels.h中KEYCODES[]数组里添加按键标签和键值映射信息。

static const KeycodeLabel KEYCODES[] = {
 { "SOFT_LEFT", 1 }, 
 { "SOFT_RIGHT", 2 },  
 { "CUSTOM", 222 },
 { NULL, 0 }
};

(5) base/core/java/android/view/KeyEvent.java中添加按键编码,这个给android空间用的。

 
 
public class KeyEvent extends InputEvent implements Parcelable { 
 public static final int KEYCODE_UNKNOWN = 0; 
 public static final int KEYCODE_SOFT_LEFT = 1;  
 public static final int KEYCODE_SOFT_RIGHT = 2;  
 ......  
 public static final int KEYCODE_CUSTOM = 222;
}

一般需要修改private static final int LAST_KEYCODE = KEYCODE_CUSTOM;

(6) base/core/res/res/values/attrs.xml配置文件,增加按键信息。

<enum name="KEYCODE_CUSTOM" value="222" />

(7) 如果是系统按键base/core/java/android/view/KeyEvent.java中
isSystem()->native_isSystemKey(mKeyCode)->
base/core/jni/android_view_KeyEvent.cpp

bool KeyEvent::isSystemKey(int32_t keyCode) { 
 switch (keyCode) 
 { 
  case AKEYCODE_MENU:
  case AKEYCODE_SOFT_RIGHT: 
  case AKEYCODE_HOME: 
  case AKEYCODE_BACK: 
  case AKEYCODE_CALL: 
  case AKEYCODE_CUSTOM: 
  return true; 
 } 
 return false;
}

(8) 如果需要修改external/webkit/WebKit/android/plugins/ANPKeyCodes.h。

你可能感兴趣的:(android)