Merge "Support loading keyboard layout overlays from resources."
This commit is contained in:
@@ -49,6 +49,17 @@ public:
|
||||
KEYBOARD_TYPE_ALPHA = 3,
|
||||
KEYBOARD_TYPE_FULL = 4,
|
||||
KEYBOARD_TYPE_SPECIAL_FUNCTION = 5,
|
||||
KEYBOARD_TYPE_OVERLAY = 6,
|
||||
};
|
||||
|
||||
enum Format {
|
||||
// Base keyboard layout, may contain device-specific options, such as "type" declaration.
|
||||
FORMAT_BASE = 0,
|
||||
// Overlay keyboard layout, more restrictive, may be published by applications,
|
||||
// cannot override device-specific options.
|
||||
FORMAT_OVERLAY = 1,
|
||||
// Either base or overlay layout ok.
|
||||
FORMAT_ANY = 2,
|
||||
};
|
||||
|
||||
// Substitute key code and meta state for fallback action.
|
||||
@@ -58,7 +69,15 @@ public:
|
||||
};
|
||||
|
||||
/* Loads a key character map from a file. */
|
||||
static status_t load(const String8& filename, sp<KeyCharacterMap>* outMap);
|
||||
static status_t load(const String8& filename, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
/* Loads a key character map from its string contents. */
|
||||
static status_t loadContents(const String8& filename,
|
||||
const char* contents, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
/* Combines a base key character map and an overlay. */
|
||||
static sp<KeyCharacterMap> combine(const sp<KeyCharacterMap>& base,
|
||||
const sp<KeyCharacterMap>& overlay);
|
||||
|
||||
/* Returns an empty key character map. */
|
||||
static sp<KeyCharacterMap> empty();
|
||||
@@ -115,6 +134,7 @@ protected:
|
||||
private:
|
||||
struct Behavior {
|
||||
Behavior();
|
||||
Behavior(const Behavior& other);
|
||||
|
||||
/* The next behavior in the list, or NULL if none. */
|
||||
Behavior* next;
|
||||
@@ -131,6 +151,7 @@ private:
|
||||
|
||||
struct Key {
|
||||
Key();
|
||||
Key(const Key& other);
|
||||
~Key();
|
||||
|
||||
/* The single character label printed on the key, or 0 if none. */
|
||||
@@ -166,11 +187,12 @@ private:
|
||||
|
||||
KeyCharacterMap* mMap;
|
||||
Tokenizer* mTokenizer;
|
||||
Format mFormat;
|
||||
State mState;
|
||||
int32_t mKeyCode;
|
||||
|
||||
public:
|
||||
Parser(KeyCharacterMap* map, Tokenizer* tokenizer);
|
||||
Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format);
|
||||
~Parser();
|
||||
status_t parse();
|
||||
|
||||
@@ -188,6 +210,7 @@ private:
|
||||
int mType;
|
||||
|
||||
KeyCharacterMap();
|
||||
KeyCharacterMap(const KeyCharacterMap& other);
|
||||
|
||||
bool getKey(int32_t keyCode, const Key** outKey) const;
|
||||
bool getKeyBehavior(int32_t keyCode, int32_t metaState,
|
||||
@@ -195,6 +218,8 @@ private:
|
||||
|
||||
bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const;
|
||||
|
||||
static status_t load(Tokenizer* tokenizer, Format format, sp<KeyCharacterMap>* outMap);
|
||||
|
||||
static void addKey(Vector<KeyEvent>& outEvents,
|
||||
int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time);
|
||||
static void addMetaKeys(Vector<KeyEvent>& outEvents,
|
||||
|
||||
@@ -89,6 +89,13 @@ KeyCharacterMap::KeyCharacterMap() :
|
||||
mType(KEYBOARD_TYPE_UNKNOWN) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) :
|
||||
RefBase(), mType(other.mType) {
|
||||
for (size_t i = 0; i < other.mKeys.size(); i++) {
|
||||
mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
KeyCharacterMap::~KeyCharacterMap() {
|
||||
for (size_t i = 0; i < mKeys.size(); i++) {
|
||||
Key* key = mKeys.editValueAt(i);
|
||||
@@ -96,7 +103,8 @@ KeyCharacterMap::~KeyCharacterMap() {
|
||||
}
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* outMap) {
|
||||
status_t KeyCharacterMap::load(const String8& filename,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
@@ -104,31 +112,77 @@ status_t KeyCharacterMap::load(const String8& filename, sp<KeyCharacterMap>* out
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key character map file %s.", status, filename.string());
|
||||
} else {
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap();
|
||||
if (!map.get()) {
|
||||
ALOGE("Error allocating key character map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map.get(), tokenizer);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (!status) {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
status = load(tokenizer, format, outMap);
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
outMap->clear();
|
||||
|
||||
Tokenizer* tokenizer;
|
||||
status_t status = Tokenizer::fromContents(filename, contents, &tokenizer);
|
||||
if (status) {
|
||||
ALOGE("Error %d opening key character map.", status);
|
||||
} else {
|
||||
status = load(tokenizer, format, outMap);
|
||||
delete tokenizer;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
status_t KeyCharacterMap::load(Tokenizer* tokenizer,
|
||||
Format format, sp<KeyCharacterMap>* outMap) {
|
||||
status_t status = OK;
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap();
|
||||
if (!map.get()) {
|
||||
ALOGE("Error allocating key character map.");
|
||||
status = NO_MEMORY;
|
||||
} else {
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
|
||||
#endif
|
||||
Parser parser(map.get(), tokenizer, format);
|
||||
status = parser.parse();
|
||||
#if DEBUG_PARSER_PERFORMANCE
|
||||
nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
|
||||
ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.",
|
||||
tokenizer->getFilename().string(), tokenizer->getLineNumber(),
|
||||
elapsedTime / 1000000.0);
|
||||
#endif
|
||||
if (!status) {
|
||||
*outMap = map;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
|
||||
const sp<KeyCharacterMap>& overlay) {
|
||||
if (overlay == NULL) {
|
||||
return base;
|
||||
}
|
||||
if (base == NULL) {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> map = new KeyCharacterMap(*base.get());
|
||||
for (size_t i = 0; i < overlay->mKeys.size(); i++) {
|
||||
int32_t keyCode = overlay->mKeys.keyAt(i);
|
||||
Key* key = overlay->mKeys.valueAt(i);
|
||||
ssize_t oldIndex = map->mKeys.indexOfKey(keyCode);
|
||||
if (oldIndex >= 0) {
|
||||
delete map->mKeys.valueAt(oldIndex);
|
||||
map->mKeys.editValueAt(oldIndex) = new Key(*key);
|
||||
} else {
|
||||
map->mKeys.add(keyCode, new Key(*key));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> KeyCharacterMap::empty() {
|
||||
return sEmpty;
|
||||
}
|
||||
@@ -508,6 +562,11 @@ KeyCharacterMap::Key::Key() :
|
||||
label(0), number(0), firstBehavior(NULL) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Key::Key(const Key& other) :
|
||||
label(other.label), number(other.number),
|
||||
firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Key::~Key() {
|
||||
Behavior* behavior = firstBehavior;
|
||||
while (behavior) {
|
||||
@@ -524,11 +583,17 @@ KeyCharacterMap::Behavior::Behavior() :
|
||||
next(NULL), metaState(0), character(0), fallbackKeyCode(0) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Behavior::Behavior(const Behavior& other) :
|
||||
next(other.next ? new Behavior(*other.next) : NULL),
|
||||
metaState(other.metaState), character(other.character),
|
||||
fallbackKeyCode(other.fallbackKeyCode) {
|
||||
}
|
||||
|
||||
|
||||
// --- KeyCharacterMap::Parser ---
|
||||
|
||||
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) :
|
||||
mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) {
|
||||
KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) :
|
||||
mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) {
|
||||
}
|
||||
|
||||
KeyCharacterMap::Parser::~Parser() {
|
||||
@@ -588,10 +653,24 @@ status_t KeyCharacterMap::Parser::parse() {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
|
||||
ALOGE("%s: Missing required keyboard 'type' declaration.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
if (mFormat == FORMAT_BASE) {
|
||||
if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) {
|
||||
ALOGE("%s: Base keyboard layout missing required keyboard 'type' declaration.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
if (mMap->mType == KEYBOARD_TYPE_OVERLAY) {
|
||||
ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
} else if (mFormat == FORMAT_OVERLAY) {
|
||||
if (mMap->mType != KEYBOARD_TYPE_OVERLAY) {
|
||||
ALOGE("%s: Overlay keyboard layout missing required keyboard "
|
||||
"'type OVERLAY' declaration.",
|
||||
mTokenizer->getLocation().string());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
@@ -616,6 +695,8 @@ status_t KeyCharacterMap::Parser::parseType() {
|
||||
type = KEYBOARD_TYPE_FULL;
|
||||
} else if (typeToken == "SPECIAL_FUNCTION") {
|
||||
type = KEYBOARD_TYPE_SPECIAL_FUNCTION;
|
||||
} else if (typeToken == "OVERLAY") {
|
||||
type = KEYBOARD_TYPE_OVERLAY;
|
||||
} else {
|
||||
ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(),
|
||||
typeToken.string());
|
||||
|
||||
@@ -129,7 +129,8 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi
|
||||
return NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status_t status = KeyCharacterMap::load(path, &keyCharacterMap);
|
||||
status_t status = KeyCharacterMap::load(path,
|
||||
KeyCharacterMap::FORMAT_BASE, &keyCharacterMap);
|
||||
if (status) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# Copyright (C) 2012 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@@ -12,5 +26,17 @@ LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
########################
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
# Validate all key maps.
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX)
|
||||
files := frameworks/base/packages/InputDevices/res/raw/*.kcm
|
||||
|
||||
LOCAL_MODULE := validate_input_devices_keymaps
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_REQUIRED_MODULES := validatekeymaps
|
||||
|
||||
validate_input_devices_keymaps: $(files)
|
||||
$(hide) $(validatekeymaps) $(files)
|
||||
|
||||
include $(BUILD_PHONY_PACKAGE)
|
||||
|
||||
@@ -12,4 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# PLACEHOLDER CONTENT #
|
||||
#
|
||||
# English (US) keyboard layout.
|
||||
# Assumes that the base keyboard layout is already English (US).
|
||||
#
|
||||
|
||||
type OVERLAY
|
||||
|
||||
@@ -12,4 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# PLACEHOLDER CONTENT #
|
||||
#
|
||||
# English (US), Dvorak keyboard layout.
|
||||
# Assumes that the base keyboard layout is already English (US).
|
||||
#
|
||||
|
||||
type OVERLAY
|
||||
|
||||
# Test
|
||||
key A {
|
||||
label: 'X'
|
||||
base: 'x'
|
||||
shift, capslock: 'X'
|
||||
ctrl, alt, meta: none
|
||||
}
|
||||
|
||||
@@ -12,4 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# PLACEHOLDER CONTENT #
|
||||
#
|
||||
# German keyboard layout.
|
||||
#
|
||||
|
||||
type OVERLAY
|
||||
|
||||
@@ -531,11 +531,29 @@ sp<KeyCharacterMap> EventHub::getKeyCharacterMap(int32_t deviceId) const {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device) {
|
||||
if (device->combinedKeyMap != NULL) {
|
||||
return device->combinedKeyMap;
|
||||
}
|
||||
return device->keyMap.keyCharacterMap;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId,
|
||||
const sp<KeyCharacterMap>& map) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
if (device) {
|
||||
if (map != device->overlayKeyMap) {
|
||||
device->overlayKeyMap = map;
|
||||
device->combinedKeyMap = KeyCharacterMap::combine(
|
||||
device->keyMap.keyCharacterMap, map);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EventHub::vibrate(int32_t deviceId, nsecs_t duration) {
|
||||
AutoMutex _l(mLock);
|
||||
Device* device = getDeviceLocked(deviceId);
|
||||
|
||||
@@ -221,6 +221,7 @@ public:
|
||||
Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0;
|
||||
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
|
||||
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) = 0;
|
||||
|
||||
/* Control the vibrator. */
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0;
|
||||
@@ -283,6 +284,7 @@ public:
|
||||
Vector<VirtualKeyDefinition>& outVirtualKeys) const;
|
||||
|
||||
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
|
||||
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map);
|
||||
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration);
|
||||
virtual void cancelVibrate(int32_t deviceId);
|
||||
@@ -321,6 +323,9 @@ private:
|
||||
VirtualKeyMap* virtualKeyMap;
|
||||
KeyMap keyMap;
|
||||
|
||||
sp<KeyCharacterMap> overlayKeyMap;
|
||||
sp<KeyCharacterMap> combinedKeyMap;
|
||||
|
||||
bool ffEffectPlaying;
|
||||
int16_t ffEffectId; // initially -1
|
||||
|
||||
|
||||
@@ -962,6 +962,14 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config
|
||||
mContext->getEventHub()->getConfiguration(mId, &mConfiguration);
|
||||
}
|
||||
|
||||
if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) {
|
||||
sp<KeyCharacterMap> keyboardLayout =
|
||||
mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier.descriptor);
|
||||
if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) {
|
||||
bumpGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
size_t numMappers = mMappers.size();
|
||||
for (size_t i = 0; i < numMappers; i++) {
|
||||
InputMapper* mapper = mMappers[i];
|
||||
|
||||
@@ -67,6 +67,9 @@ struct InputReaderConfiguration {
|
||||
// The visible touches option changed.
|
||||
CHANGE_SHOW_TOUCHES = 1 << 3,
|
||||
|
||||
// The keyboard layouts must be reloaded.
|
||||
CHANGE_KEYBOARD_LAYOUTS = 1 << 4,
|
||||
|
||||
// All devices must be reopened.
|
||||
CHANGE_MUST_REOPEN = 1 << 31,
|
||||
};
|
||||
@@ -222,6 +225,9 @@ public:
|
||||
* and provides information about all current input devices.
|
||||
*/
|
||||
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0;
|
||||
|
||||
/* Gets the keyboard layout for a particular input device. */
|
||||
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -170,6 +170,10 @@ private:
|
||||
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) {
|
||||
mInputDevices = inputDevices;
|
||||
}
|
||||
|
||||
virtual sp<KeyCharacterMap> getKeyboardLayout(const String8& inputDeviceDescriptor) {
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -646,6 +650,10 @@ private:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void vibrate(int32_t deviceId, nsecs_t duration) {
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ import java.util.TreeMap;
|
||||
public class InputMethodManagerService extends IInputMethodManager.Stub
|
||||
implements ServiceConnection, Handler.Callback {
|
||||
static final boolean DEBUG = false;
|
||||
static final String TAG = "InputManagerService";
|
||||
static final String TAG = "InputMethodManagerService";
|
||||
|
||||
static final int MSG_SHOW_IM_PICKER = 1;
|
||||
static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.android.internal.os.SamplingProfilerIntegration;
|
||||
import com.android.internal.widget.LockSettingsService;
|
||||
import com.android.server.accessibility.AccessibilityManagerService;
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
import com.android.server.input.InputManagerService;
|
||||
import com.android.server.net.NetworkPolicyManagerService;
|
||||
import com.android.server.net.NetworkStatsService;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
@@ -137,6 +138,7 @@ class ServerThread extends Thread {
|
||||
ThrottleService throttle = null;
|
||||
NetworkTimeUpdateService networkTimeUpdater = null;
|
||||
CommonTimeManagementService commonTimeMgmtService = null;
|
||||
InputManagerService inputManager = null;
|
||||
|
||||
// Critical services...
|
||||
try {
|
||||
@@ -224,7 +226,8 @@ class ServerThread extends Thread {
|
||||
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL,
|
||||
!firstBoot);
|
||||
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
|
||||
ServiceManager.addService(Context.INPUT_SERVICE, wm.getInputManagerService());
|
||||
inputManager = wm.getInputManagerService();
|
||||
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
|
||||
|
||||
ActivityManagerService.self().setWindowManager(wm);
|
||||
|
||||
@@ -722,6 +725,7 @@ class ServerThread extends Thread {
|
||||
final TextServicesManagerService textServiceManagerServiceF = tsms;
|
||||
final StatusBarManagerService statusBarF = statusBar;
|
||||
final DreamManagerService dreamyF = dreamy;
|
||||
final InputManagerService inputManagerF = inputManager;
|
||||
|
||||
// We now tell the activity manager it is okay to run third party
|
||||
// code. It will call back into us once it has gotten to the state
|
||||
@@ -833,6 +837,11 @@ class ServerThread extends Thread {
|
||||
} catch (Throwable e) {
|
||||
reportWtf("making DreamManagerService ready", e);
|
||||
}
|
||||
try {
|
||||
if (inputManagerF != null) inputManagerF.systemReady();
|
||||
} catch (Throwable e) {
|
||||
reportWtf("making InputManagerService ready", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -26,15 +26,18 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.database.ContentObserver;
|
||||
@@ -60,13 +63,11 @@ import android.util.Xml;
|
||||
import android.view.InputChannel;
|
||||
import android.view.InputDevice;
|
||||
import android.view.InputEvent;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.Surface;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManagerPolicy;
|
||||
import android.view.KeyCharacterMap.UnavailableException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
@@ -77,13 +78,14 @@ import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.io.Streams;
|
||||
import libcore.util.Objects;
|
||||
|
||||
/*
|
||||
@@ -91,7 +93,7 @@ import libcore.util.Objects;
|
||||
*/
|
||||
public class InputManagerService extends IInputManager.Stub implements Watchdog.Monitor {
|
||||
static final String TAG = "InputManager";
|
||||
static final boolean DEBUG = false;
|
||||
static final boolean DEBUG = true;
|
||||
|
||||
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
|
||||
|
||||
@@ -103,6 +105,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
private final Context mContext;
|
||||
private final Callbacks mCallbacks;
|
||||
private final InputManagerHandler mHandler;
|
||||
private boolean mSystemReady;
|
||||
|
||||
// Persistent data store. Must be locked each time during use.
|
||||
private final PersistentDataStore mDataStore = new PersistentDataStore();
|
||||
@@ -163,6 +166,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
private static native void nativeVibrate(int ptr, int deviceId, long[] pattern,
|
||||
int repeat, int token);
|
||||
private static native void nativeCancelVibrate(int ptr, int deviceId, int token);
|
||||
private static native void nativeReloadKeyboardLayouts(int ptr);
|
||||
private static native String nativeDump(int ptr);
|
||||
private static native void nativeMonitor(int ptr);
|
||||
|
||||
@@ -212,7 +216,33 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
updatePointerSpeedFromSettings();
|
||||
updateShowTouchesFromSettings();
|
||||
}
|
||||
|
||||
|
||||
public void systemReady() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "System ready.");
|
||||
}
|
||||
mSystemReady = true;
|
||||
reloadKeyboardLayouts();
|
||||
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addDataScheme("package");
|
||||
mContext.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Packages changed, reloading keyboard layouts.");
|
||||
}
|
||||
reloadKeyboardLayouts();
|
||||
}
|
||||
}, filter, null, mHandler);
|
||||
}
|
||||
|
||||
private void reloadKeyboardLayouts() {
|
||||
nativeReloadKeyboardLayouts(mPtr);
|
||||
}
|
||||
|
||||
public void setDisplaySize(int displayId, int width, int height,
|
||||
int externalWidth, int externalHeight) {
|
||||
if (width <= 0 || height <= 0 || externalWidth <= 0 || externalHeight <= 0) {
|
||||
@@ -528,14 +558,14 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
|
||||
@Override // Binder call
|
||||
public KeyboardLayout[] getKeyboardLayouts() {
|
||||
ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
|
||||
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
|
||||
for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
|
||||
PackageManager.GET_META_DATA)) {
|
||||
loadKeyboardLayouts(pm, resolveInfo.activityInfo, list, null);
|
||||
}
|
||||
final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
|
||||
visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
|
||||
@Override
|
||||
public void visitKeyboardLayout(Resources resources,
|
||||
String descriptor, String label, int kcmResId) {
|
||||
list.add(new KeyboardLayout(descriptor, label));
|
||||
}
|
||||
});
|
||||
return list.toArray(new KeyboardLayout[list.size()]);
|
||||
}
|
||||
|
||||
@@ -545,37 +575,57 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
|
||||
}
|
||||
|
||||
KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
|
||||
if (d == null) {
|
||||
return null;
|
||||
final KeyboardLayout[] result = new KeyboardLayout[1];
|
||||
visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
|
||||
@Override
|
||||
public void visitKeyboardLayout(Resources resources,
|
||||
String descriptor, String label, int kcmResId) {
|
||||
result[0] = new KeyboardLayout(descriptor, label);
|
||||
}
|
||||
});
|
||||
if (result[0] == null) {
|
||||
Log.w(TAG, "Could not get keyboard layout with descriptor '"
|
||||
+ keyboardLayoutDescriptor + "'.");
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private void visitAllKeyboardLayouts(KeyboardLayoutVisitor visitor) {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
try {
|
||||
ActivityInfo receiver = pm.getReceiverInfo(
|
||||
new ComponentName(d.packageName, d.receiverName),
|
||||
PackageManager.GET_META_DATA);
|
||||
return loadKeyboardLayouts(pm, receiver, null, d.keyboardLayoutName);
|
||||
} catch (NameNotFoundException ex) {
|
||||
Log.w(TAG, "Could not load keyboard layout '" + d.keyboardLayoutName
|
||||
+ "' from receiver " + d.packageName + "/" + d.receiverName, ex);
|
||||
return null;
|
||||
Intent intent = new Intent(InputManager.ACTION_QUERY_KEYBOARD_LAYOUTS);
|
||||
for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent,
|
||||
PackageManager.GET_META_DATA)) {
|
||||
visitKeyboardLayoutsInPackage(pm, resolveInfo.activityInfo, null, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyboardLayout loadKeyboardLayouts(
|
||||
PackageManager pm, ActivityInfo receiver,
|
||||
List<KeyboardLayout> list, String keyboardName) {
|
||||
private void visitKeyboardLayout(String keyboardLayoutDescriptor,
|
||||
KeyboardLayoutVisitor visitor) {
|
||||
KeyboardLayoutDescriptor d = KeyboardLayoutDescriptor.parse(keyboardLayoutDescriptor);
|
||||
if (d != null) {
|
||||
final PackageManager pm = mContext.getPackageManager();
|
||||
try {
|
||||
ActivityInfo receiver = pm.getReceiverInfo(
|
||||
new ComponentName(d.packageName, d.receiverName),
|
||||
PackageManager.GET_META_DATA);
|
||||
visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, visitor);
|
||||
} catch (NameNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void visitKeyboardLayoutsInPackage(PackageManager pm, ActivityInfo receiver,
|
||||
String keyboardName, KeyboardLayoutVisitor visitor) {
|
||||
Bundle metaData = receiver.metaData;
|
||||
if (metaData == null) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
int configResId = metaData.getInt(InputManager.META_DATA_KEYBOARD_LAYOUTS);
|
||||
if (configResId == 0) {
|
||||
Log.w(TAG, "Missing meta-data '" + InputManager.META_DATA_KEYBOARD_LAYOUTS
|
||||
+ "' on receiver " + receiver.packageName + "/" + receiver.name);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -608,12 +658,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
} else {
|
||||
String descriptor = KeyboardLayoutDescriptor.format(
|
||||
receiver.packageName, receiver.name, name);
|
||||
KeyboardLayout c = new KeyboardLayout(descriptor, label);
|
||||
if (keyboardName != null && name.equals(keyboardName)) {
|
||||
return c;
|
||||
}
|
||||
if (list != null) {
|
||||
list.add(c);
|
||||
if (keyboardName == null || name.equals(keyboardName)) {
|
||||
visitor.visitKeyboardLayout(resources, descriptor,
|
||||
label, kcmResId);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -629,16 +676,9 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
parser.close();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Log.w(TAG, "Could not load keyboard layout resource from receiver "
|
||||
Log.w(TAG, "Could not parse keyboard layout resource from receiver "
|
||||
+ receiver.packageName + "/" + receiver.name, ex);
|
||||
return null;
|
||||
}
|
||||
if (keyboardName != null) {
|
||||
Log.w(TAG, "Could not load keyboard layout '" + keyboardName
|
||||
+ "' from receiver " + receiver.packageName + "/" + receiver.name
|
||||
+ " because it was not declared in the keyboard layout resource.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override // Binder call
|
||||
@@ -664,52 +704,24 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
|
||||
}
|
||||
|
||||
final boolean changed;
|
||||
synchronized (mDataStore) {
|
||||
try {
|
||||
mDataStore.setKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor);
|
||||
changed = mDataStore.setKeyboardLayout(
|
||||
inputDeviceDescriptor, keyboardLayoutDescriptor);
|
||||
} finally {
|
||||
mDataStore.saveIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts.");
|
||||
}
|
||||
reloadKeyboardLayouts();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the key character map associated with the keyboard layout.
|
||||
*
|
||||
* @param pm The package manager.
|
||||
* @return The key character map, or null if it could not be loaded for any reason.
|
||||
*
|
||||
public KeyCharacterMap loadKeyCharacterMap(PackageManager pm) {
|
||||
if (pm == null) {
|
||||
throw new IllegalArgumentException("pm must not be null");
|
||||
}
|
||||
|
||||
if (mKeyCharacterMap == null) {
|
||||
KeyboardLayoutDescriptor d = InputManager.parseKeyboardLayoutDescriptor(mDescriptor);
|
||||
if (d == null) {
|
||||
Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
|
||||
+ "' because the descriptor could not be parsed.");
|
||||
return null;
|
||||
}
|
||||
|
||||
CharSequence cs = pm.getText(d.packageName, mKeyCharacterMapResId, null);
|
||||
if (cs == null) {
|
||||
Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
|
||||
+ "' because its associated resource could not be loaded.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
mKeyCharacterMap = KeyCharacterMap.load(cs);
|
||||
} catch (UnavailableException ex) {
|
||||
Log.e(InputManager.TAG, "Could not load key character map '" + mDescriptor
|
||||
+ "' due to an error while parsing.", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return mKeyCharacterMap;
|
||||
}*/
|
||||
|
||||
public void setInputWindows(InputWindowHandle[] windowHandles) {
|
||||
nativeSetInputWindows(mPtr, windowHandles);
|
||||
}
|
||||
@@ -1076,6 +1088,40 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
return PointerIcon.getDefaultIcon(mContext);
|
||||
}
|
||||
|
||||
// Native callback.
|
||||
private String[] getKeyboardLayoutOverlay(String inputDeviceDescriptor) {
|
||||
if (!mSystemReady) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
|
||||
if (keyboardLayoutDescriptor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String[] result = new String[2];
|
||||
visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
|
||||
@Override
|
||||
public void visitKeyboardLayout(Resources resources,
|
||||
String descriptor, String label, int kcmResId) {
|
||||
try {
|
||||
result[0] = descriptor;
|
||||
result[1] = Streams.readFully(new InputStreamReader(
|
||||
resources.openRawResource(kcmResId)));
|
||||
} catch (IOException ex) {
|
||||
} catch (NotFoundException ex) {
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result[0] == null) {
|
||||
Log.w(TAG, "Could not get keyboard layout with descriptor '"
|
||||
+ keyboardLayoutDescriptor + "'.");
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback interface implemented by the Window Manager.
|
||||
*/
|
||||
@@ -1169,6 +1215,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog.
|
||||
}
|
||||
}
|
||||
|
||||
private interface KeyboardLayoutVisitor {
|
||||
void visitKeyboardLayout(Resources resources,
|
||||
String descriptor, String label, int kcmResId);
|
||||
}
|
||||
|
||||
private final class InputDevicesChangedListenerRecord implements DeathRecipient {
|
||||
private final int mPid;
|
||||
private final IInputDevicesChangedListener mListener;
|
||||
|
||||
@@ -22,7 +22,8 @@ LOCAL_C_INCLUDES += \
|
||||
$(JNI_H_INCLUDE) \
|
||||
frameworks/base/services \
|
||||
frameworks/base/core/jni \
|
||||
external/skia/include/core
|
||||
external/skia/include/core \
|
||||
libcore/include
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libandroid_runtime \
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
#include <android_view_PointerIcon.h>
|
||||
#include <android/graphics/GraphicsJNI.h>
|
||||
|
||||
#include <ScopedLocalRef.h>
|
||||
#include <ScopedUtfChars.h>
|
||||
|
||||
#include "com_android_server_PowerManagerService.h"
|
||||
#include "com_android_server_input_InputApplicationHandle.h"
|
||||
#include "com_android_server_input_InputWindowHandle.h"
|
||||
@@ -79,6 +82,7 @@ static struct {
|
||||
jmethodID getLongPressTimeout;
|
||||
jmethodID getPointerLayer;
|
||||
jmethodID getPointerIcon;
|
||||
jmethodID getKeyboardLayoutOverlay;
|
||||
} gServiceClassInfo;
|
||||
|
||||
static struct {
|
||||
@@ -179,12 +183,14 @@ public:
|
||||
void setSystemUiVisibility(int32_t visibility);
|
||||
void setPointerSpeed(int32_t speed);
|
||||
void setShowTouches(bool enabled);
|
||||
void reloadKeyboardLayouts();
|
||||
|
||||
/* --- InputReaderPolicyInterface implementation --- */
|
||||
|
||||
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
|
||||
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
|
||||
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
|
||||
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor);
|
||||
|
||||
/* --- InputDispatcherPolicyInterface implementation --- */
|
||||
|
||||
@@ -522,6 +528,29 @@ void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>
|
||||
checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged");
|
||||
}
|
||||
|
||||
sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay(
|
||||
const String8& inputDeviceDescriptor) {
|
||||
JNIEnv* env = jniEnv();
|
||||
|
||||
sp<KeyCharacterMap> result;
|
||||
ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
|
||||
ScopedLocalRef<jobjectArray> arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj,
|
||||
gServiceClassInfo.getKeyboardLayoutOverlay, descriptorObj.get())));
|
||||
if (arrayObj.get()) {
|
||||
ScopedLocalRef<jstring> filenameObj(env,
|
||||
jstring(env->GetObjectArrayElement(arrayObj.get(), 0)));
|
||||
ScopedLocalRef<jstring> contentsObj(env,
|
||||
jstring(env->GetObjectArrayElement(arrayObj.get(), 1)));
|
||||
ScopedUtfChars filenameChars(env, filenameObj.get());
|
||||
ScopedUtfChars contentsChars(env, contentsObj.get());
|
||||
|
||||
KeyCharacterMap::loadContents(String8(filenameChars.c_str()),
|
||||
String8(contentsChars.c_str()), KeyCharacterMap::FORMAT_OVERLAY, &result);
|
||||
}
|
||||
checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay");
|
||||
return result;
|
||||
}
|
||||
|
||||
void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode,
|
||||
int32_t switchValue, uint32_t policyFlags) {
|
||||
#if DEBUG_INPUT_DISPATCHER_POLICY
|
||||
@@ -728,6 +757,11 @@ void NativeInputManager::setShowTouches(bool enabled) {
|
||||
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
|
||||
}
|
||||
|
||||
void NativeInputManager::reloadKeyboardLayouts() {
|
||||
mInputManager->getReader()->requestRefreshConfiguration(
|
||||
InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
|
||||
}
|
||||
|
||||
bool NativeInputManager::isScreenOn() {
|
||||
return android_server_PowerManagerService_isScreenOn();
|
||||
}
|
||||
@@ -1258,6 +1292,13 @@ static void nativeCancelVibrate(JNIEnv* env,
|
||||
im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
|
||||
}
|
||||
|
||||
static void nativeReloadKeyboardLayouts(JNIEnv* env,
|
||||
jclass clazz, jint ptr) {
|
||||
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
|
||||
|
||||
im->reloadKeyboardLayouts();
|
||||
}
|
||||
|
||||
static jstring nativeDump(JNIEnv* env, jclass clazz, jint ptr) {
|
||||
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
|
||||
|
||||
@@ -1323,6 +1364,8 @@ static JNINativeMethod gInputManagerMethods[] = {
|
||||
(void*) nativeVibrate },
|
||||
{ "nativeCancelVibrate", "(III)V",
|
||||
(void*) nativeCancelVibrate },
|
||||
{ "nativeReloadKeyboardLayouts", "(I)V",
|
||||
(void*) nativeReloadKeyboardLayouts },
|
||||
{ "nativeDump", "(I)Ljava/lang/String;",
|
||||
(void*) nativeDump },
|
||||
{ "nativeMonitor", "(I)V",
|
||||
@@ -1418,6 +1461,9 @@ int register_android_server_InputManager(JNIEnv* env) {
|
||||
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
|
||||
"getPointerIcon", "()Landroid/view/PointerIcon;");
|
||||
|
||||
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
|
||||
"getKeyboardLayoutOverlay", "(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
|
||||
// InputDevice
|
||||
|
||||
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
|
||||
|
||||
@@ -89,7 +89,8 @@ static bool validateFile(const char* filename) {
|
||||
|
||||
case FILETYPE_KEYCHARACTERMAP: {
|
||||
sp<KeyCharacterMap> map;
|
||||
status_t status = KeyCharacterMap::load(String8(filename), &map);
|
||||
status_t status = KeyCharacterMap::load(String8(filename),
|
||||
KeyCharacterMap::FORMAT_ANY, &map);
|
||||
if (status) {
|
||||
fprintf(stderr, "Error %d parsing key character map file.\n\n", status);
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user