Skip to content

Commit 420489c

Browse files
Jeff BrownAndroid (Google) Code Review
authored andcommitted
Merge "Support loading keyboard layout overlays from resources."
2 parents 4db3ca7 + 6ec6f79 commit 420489c

File tree

18 files changed

+423
-115
lines changed

18 files changed

+423
-115
lines changed

include/androidfw/KeyCharacterMap.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ class KeyCharacterMap : public RefBase {
4949
KEYBOARD_TYPE_ALPHA = 3,
5050
KEYBOARD_TYPE_FULL = 4,
5151
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
52+
KEYBOARD_TYPE_OVERLAY = 6,
53+
};
54+
55+
enum Format {
56+
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
57+
FORMAT_BASE = 0,
58+
// Overlay keyboard layout, more restrictive, may be published by applications,
59+
// cannot override device-specific options.
60+
FORMAT_OVERLAY = 1,
61+
// Either base or overlay layout ok.
62+
FORMAT_ANY = 2,
5263
};
5364

5465
// Substitute key code and meta state for fallback action.
@@ -58,7 +69,15 @@ class KeyCharacterMap : public RefBase {
5869
};
5970

6071
/* Loads a key character map from a file. */
61-
static status_t load(const String8& filename, sp<KeyCharacterMap>* outMap);
72+
static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
73+
74+
/* Loads a key character map from its string contents. */
75+
static status_t loadContents(const String8& filename,
76+
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
77+
78+
/* Combines a base key character map and an overlay. */
79+
static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
80+
const sp<KeyCharacterMap>& overlay);
6281

6382
/* Returns an empty key character map. */
6483
static sp<KeyCharacterMap> empty();
@@ -115,6 +134,7 @@ class KeyCharacterMap : public RefBase {
115134
private:
116135
struct Behavior {
117136
Behavior();
137+
Behavior(const Behavior& other);
118138

119139
/* The next behavior in the list, or NULL if none. */
120140
Behavior* next;
@@ -131,6 +151,7 @@ class KeyCharacterMap : public RefBase {
131151

132152
struct Key {
133153
Key();
154+
Key(const Key& other);
134155
~Key();
135156

136157
/* The single character label printed on the key, or 0 if none. */
@@ -166,11 +187,12 @@ class KeyCharacterMap : public RefBase {
166187

167188
KeyCharacterMap* mMap;
168189
Tokenizer* mTokenizer;
190+
Format mFormat;
169191
State mState;
170192
int32_t mKeyCode;
171193

172194
public:
173-
Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
195+
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
174196
~Parser();
175197
status_t parse();
176198

@@ -188,13 +210,16 @@ class KeyCharacterMap : public RefBase {
188210
int mType;
189211

190212
KeyCharacterMap();
213+
KeyCharacterMap(const KeyCharacterMap& other);
191214

192215
bool getKey(int32_t keyCode, const Key** outKey) const;
193216
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
194217
const Key** outKey, const Behavior** outBehavior) const;
195218

196219
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
197220

221+
static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
222+
198223
static void addKey(Vector<KeyEvent>& outEvents,
199224
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
200225
static void addMetaKeys(Vector<KeyEvent>& outEvents,

libs/androidfw/KeyCharacterMap.cpp

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -89,46 +89,100 @@ KeyCharacterMap::KeyCharacterMap() :
8989
mType(KEYBOARD_TYPE_UNKNOWN) {
9090
}
9191

92+
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
93+
RefBase(), mType(other.mType) {
94+
for (size_t i = 0; i < other.mKeys.size(); i++) {
95+
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
96+
}
97+
}
98+
9299
KeyCharacterMap::~KeyCharacterMap() {
93100
for (size_t i = 0; i < mKeys.size(); i++) {
94101
Key* key = mKeys.editValueAt(i);
95102
delete key;
96103
}
97104
}
98105

99-
status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* outMap) {
106+
status_t KeyCharacterMap::load(const String8& filename,
107+
Format format, sp<KeyCharacterMap>* outMap) {
100108
outMap->clear();
101109

102110
Tokenizer* tokenizer;
103111
status_t status = Tokenizer::open(filename, &tokenizer);
104112
if (status) {
105113
ALOGE("Error %d opening key character map file %s.", status, filename.string());
106114
} else {
107-
sp<KeyCharacterMap> map = new KeyCharacterMap();
108-
if (!map.get()) {
109-
ALOGE("Error allocating key character map.");
110-
status = NO_MEMORY;
111-
} else {
115+
status = load(tokenizer, format, outMap);
116+
delete tokenizer;
117+
}
118+
return status;
119+
}
120+
121+
status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
122+
Format format, sp<KeyCharacterMap>* outMap) {
123+
outMap->clear();
124+
125+
Tokenizer* tokenizer;
126+
status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
127+
if (status) {
128+
ALOGE("Error %d opening key character map.", status);
129+
} else {
130+
status = load(tokenizer, format, outMap);
131+
delete tokenizer;
132+
}
133+
return status;
134+
}
135+
136+
status_t KeyCharacterMap::load(Tokenizer* tokenizer,
137+
Format format, sp<KeyCharacterMap>* outMap) {
138+
status_t status = OK;
139+
sp<KeyCharacterMap> map = new KeyCharacterMap();
140+
if (!map.get()) {
141+
ALOGE("Error allocating key character map.");
142+
status = NO_MEMORY;
143+
} else {
112144
#if DEBUG_PARSER_PERFORMANCE
113-
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
145+
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
114146
#endif
115-
Parser parser(map.get(), tokenizer);
116-
status = parser.parse();
147+
Parser parser(map.get(), tokenizer, format);
148+
status = parser.parse();
117149
#if DEBUG_PARSER_PERFORMANCE
118-
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
119-
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
120-
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
121-
elapsedTime / 1000000.0);
150+
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
151+
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
152+
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
153+
elapsedTime / 1000000.0);
122154
#endif
123-
if (!status) {
124-
*outMap = map;
125-
}
155+
if (!status) {
156+
*outMap = map;
126157
}
127-
delete tokenizer;
128158
}
129159
return status;
130160
}
131161

162+
sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
163+
const sp<KeyCharacterMap>& overlay) {
164+
if (overlay == NULL) {
165+
return base;
166+
}
167+
if (base == NULL) {
168+
return overlay;
169+
}
170+
171+
sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
172+
for (size_t i = 0; i < overlay->mKeys.size(); i++) {
173+
int32_t keyCode = overlay->mKeys.keyAt(i);
174+
Key* key = overlay->mKeys.valueAt(i);
175+
ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
176+
if (oldIndex >= 0) {
177+
delete map->mKeys.valueAt(oldIndex);
178+
map->mKeys.editValueAt(oldIndex) = new Key(*key);
179+
} else {
180+
map->mKeys.add(keyCode, new Key(*key));
181+
}
182+
}
183+
return map;
184+
}
185+
132186
sp<KeyCharacterMap> KeyCharacterMap::empty() {
133187
return sEmpty;
134188
}
@@ -508,6 +562,11 @@ KeyCharacterMap::Key::Key() :
508562
label(0), number(0), firstBehavior(NULL) {
509563
}
510564

565+
KeyCharacterMap::Key::Key(const Key& other) :
566+
label(other.label), number(other.number),
567+
firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
568+
}
569+
511570
KeyCharacterMap::Key::~Key() {
512571
Behavior* behavior = firstBehavior;
513572
while (behavior) {
@@ -524,11 +583,17 @@ KeyCharacterMap::Behavior::Behavior() :
524583
next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
525584
}
526585

586+
KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
587+
next(other.next ? new Behavior(*other.next) : NULL),
588+
metaState(other.metaState), character(other.character),
589+
fallbackKeyCode(other.fallbackKeyCode) {
590+
}
591+
527592

528593
// --- KeyCharacterMap::Parser ---
529594

530-
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
531-
mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
595+
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
596+
mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
532597
}
533598

534599
KeyCharacterMap::Parser::~Parser() {
@@ -588,10 +653,24 @@ status_t KeyCharacterMap::Parser::parse() {
588653
return BAD_VALUE;
589654
}
590655

591-
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
592-
ALOGE("%s: Missing required keyboard 'type' declaration.",
593-
mTokenizer->getLocation().string());
594-
return BAD_VALUE;
656+
if (mFormat == FORMAT_BASE) {
657+
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
658+
ALOGE("%s: Base keyboard layout missing required keyboard 'type' declaration.",
659+
mTokenizer->getLocation().string());
660+
return BAD_VALUE;
661+
}
662+
if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
663+
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
664+
mTokenizer->getLocation().string());
665+
return BAD_VALUE;
666+
}
667+
} else if (mFormat == FORMAT_OVERLAY) {
668+
if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
669+
ALOGE("%s: Overlay keyboard layout missing required keyboard "
670+
"'type OVERLAY' declaration.",
671+
mTokenizer->getLocation().string());
672+
return BAD_VALUE;
673+
}
595674
}
596675

597676
return NO_ERROR;
@@ -616,6 +695,8 @@ status_t KeyCharacterMap::Parser::parseType() {
616695
type = KEYBOARD_TYPE_FULL;
617696
} else if (typeToken == "SPECIAL_FUNCTION") {
618697
type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
698+
} else if (typeToken == "OVERLAY") {
699+
type = KEYBOARD_TYPE_OVERLAY;
619700
} else {
620701
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
621702
typeToken.string());

libs/androidfw/Keyboard.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
129129
return NAME_NOT_FOUND;
130130
}
131131

132-
status_t status = KeyCharacterMap::load(path, &keyCharacterMap);
132+
status_t status = KeyCharacterMap::load(path,
133+
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
133134
if (status) {
134135
return status;
135136
}

packages/InputDevices/Android.mk

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# Copyright (C) 2012 The Android Open Source Project
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
115
LOCAL_PATH:= $(call my-dir)
216
include $(CLEAR_VARS)
317

@@ -12,5 +26,17 @@ LOCAL_CERTIFICATE := platform
1226

1327
include $(BUILD_PACKAGE)
1428

15-
########################
16-
include $(call all-makefiles-under,$(LOCAL_PATH))
29+
# Validate all key maps.
30+
include $(CLEAR_VARS)
31+
32+
validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
33+
files := frameworks/base/packages/InputDevices/res/raw/*.kcm
34+
35+
LOCAL_MODULE := validate_input_devices_keymaps
36+
LOCAL_MODULE_TAGS := optional
37+
LOCAL_REQUIRED_MODULES := validatekeymaps
38+
39+
validate_input_devices_keymaps: $(files)
40+
$(hide) $(validatekeymaps) $(files)
41+
42+
include $(BUILD_PHONY_PACKAGE)

packages/InputDevices/res/raw/keyboard_layout_english_us.kcm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,9 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# English (US) keyboard layout.
17+
# Assumes that the base keyboard layout is already English (US).
18+
#
19+
20+
type OVERLAY

packages/InputDevices/res/raw/keyboard_layout_english_us_dvorak.kcm

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,17 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# English (US), Dvorak keyboard layout.
17+
# Assumes that the base keyboard layout is already English (US).
18+
#
19+
20+
type OVERLAY
21+
22+
# Test
23+
key A {
24+
label: 'X'
25+
base: 'x'
26+
shift, capslock: 'X'
27+
ctrl, alt, meta: none
28+
}

packages/InputDevices/res/raw/keyboard_layout_german.kcm

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# PLACEHOLDER CONTENT #
15+
#
16+
# German keyboard layout.
17+
#
18+
19+
type OVERLAY

services/input/EventHub.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,29 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
531531
AutoMutex _l(mLock);
532532
Device* device = getDeviceLocked(deviceId);
533533
if (device) {
534+
if (device->combinedKeyMap != NULL) {
535+
return device->combinedKeyMap;
536+
}
534537
return device->keyMap.keyCharacterMap;
535538
}
536539
return NULL;
537540
}
538541

542+
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
543+
const sp<KeyCharacterMap>& map) {
544+
AutoMutex _l(mLock);
545+
Device* device = getDeviceLocked(deviceId);
546+
if (device) {
547+
if (map != device->overlayKeyMap) {
548+
device->overlayKeyMap = map;
549+
device->combinedKeyMap = KeyCharacterMap::combine(
550+
device->keyMap.keyCharacterMap, map);
551+
return true;
552+
}
553+
}
554+
return false;
555+
}
556+
539557
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
540558
AutoMutex _l(mLock);
541559
Device* device = getDeviceLocked(deviceId);

0 commit comments

Comments
 (0)