需求:根据系统语言,确定外接物理键盘布局。
外接键盘的布局由以下文件决定:
1./system/usr/keylayout/Generic.kl
2./system/usr/keychars/Generic.kcm
首先,得准备好这两个文件,以德国为例:de_keylayout.kl , de_keylayout.kcm (命名根据自己的程序来定)
调用流程如下:
setting---->mWindowManager.forceScanDevices(language)--->mInputManager.forceScanDevices(language)---->nativeForceScanDevices(language)
------>android_server_InputManager_nativeForceScanDevices(JNIEnv* env, jclass clazz, jstring language)
------>gNativeInputManager->getInputManager()->forceScanDevices(languageStr)
------>mReader->forceScanDevices(language)
------>mEventHub->forceScanDevicesLocked(language)
从jni开始:
static void android_server_InputManager_nativeForceScanDevices(JNIEnv* env, jclass clazz, jstring language) {
if (checkInputManagerUnitialized(env)) {
return;
}
const char *languageStr = env->GetStringUTFChars(language, NULL);
gNativeInputManager->getInputManager()->forceScanDevices(languageStr);
env->ReleaseStringUTFChars(language, languageStr);
}
void InputManager::forceScanDevices(const char* language) {
mReader->forceScanDevices(language);
}
void InputReader::forceScanDevices(const char* language) {
mEventHub->forceScanDevicesLocked(language);
}
void EventHub::forceScanDevicesLocked(const char* lang) {
language = lang;
closeAllDevicesLocked();//必须先把设备关了
status_t res = scanDirLocked(DEVICE_PATH);//重新扫描设备
if(res < 0) {
LOGE("force scan dir failed for %s\n", DEVICE_PATH);
}
}
status_t EventHub::loadKeyMapLocked(Device* device, const char* language) {
return device->keyMap.load(device->identifier, device->configuration, language);
}
Keyboard.cpp:
status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier,
const PropertyMap* deviceConfiguration, const char* language) {
.....
// Try searching by device identifier.
if (probeKeyMap(deviceIdenfifier, String8::empty())) {
return OK;
}
char lan[PROPERTY_VALUE_MAX];
property_get("persist.sys.sa.lan", lan, NULL);
LOGD("----persist.sys.sa.lan: %s", lan);
if (lan != NULL && strcmp(lan, "")) {
if (probeKeyMap(deviceIdenfifier, String8(lan) + String8("_keyboard"))) {
return OK;
}
} else {
if (language == NULL || !strcmp(language, "unknow")) {
char property[PROPERTY_VALUE_MAX];
property_get("persist.sys.language", property, NULL);
if (probeKeyMap(deviceIdenfifier, String8(property) + String8("_keyboard"))) {
return OK;
}
} else {
if (probeKeyMap(deviceIdenfifier, String8(language) + String8("_keyboard"))) {
return OK;
}
}
}
// Fall back on the Generic key map.
// TODO Apply some additional heuristics here to figure out what kind of
// generic key map to use (US English, etc.) for typical external keyboards.
if (probeKeyMap(deviceIdenfifier, String8("Generic"))) {
return OK;
}
....
}
这样就能找到对应的kl文件了。如:de_keylayout.kl
设置中的UI:
/*
* Copyright (C) 2010 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.
*/
package com.android.settings.inputmethod;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.IWindowManager;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class InputMethodAndSubtypeEnablerForExternalKeyboard extends SettingsPreferenceFragment implements
Preference.OnPreferenceChangeListener {
private static final String TAG =InputMethodAndSubtypeEnablerForExternalKeyboard.class.getSimpleName();
final private HashMap<String, CheckBoxPreference> mSubtypeAutoSelectionCBMap =
new HashMap<String, CheckBoxPreference>();
final private HashMap<String, String> mKeyboardEntryMap = new HashMap<String, String>();
private String mInputMethodId;
private String mTitle;
private String mSystemLanguage = "";
private ListPreference mLanguageList;
private static final String default_language_value = "en";
private static final String keyboard_name = " Keyboard";
private static final String default_language_keyboard = "English";
private boolean isSystemLanguage;
private boolean isSelectKeyboard;
private String valLanguage;
private IWindowManager mWindowManager;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Configuration config = getResources().getConfiguration();
final Bundle arguments = getArguments();
// Input method id should be available from an Intent when this preference is launched as a
// single Activity (see InputMethodAndSubtypeEnablerActivity). It should be available
// from a preference argument when the preference is launched as a part of the other
// Activity (like a right pane of 2-pane Settings app)
mInputMethodId = getActivity().getIntent().getStringExtra(
android.provider.Settings.EXTRA_INPUT_METHOD_ID);
if (mInputMethodId == null && (arguments != null)) {
final String inputMethodId =
arguments.getString(android.provider.Settings.EXTRA_INPUT_METHOD_ID);
if (inputMethodId != null) {
mInputMethodId = inputMethodId;
}
}
mTitle = getActivity().getIntent().getStringExtra(Intent.EXTRA_TITLE);
if (mTitle == null && (arguments != null)) {
final String title = arguments.getString(Intent.EXTRA_TITLE);
if (title != null) {
mTitle = title;
}
}
mWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
mSystemLanguage = config.locale.getLanguage();
setPreferenceScreen(createPreferenceHierarchy());
}
@Override
public void onActivityCreated(Bundle icicle) {
super.onActivityCreated(icicle);
if (!TextUtils.isEmpty(mTitle)) {
getActivity().setTitle(mTitle);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
Settings.System.putInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 0);
String value = String.valueOf(newValue);
Settings.System.putString(getContentResolver(), Settings.System.EXTERNAL_KEYBOARD_VALUE, value);
SystemProperties.set("persist.sys.sa.lan", value);
try {
mWindowManager.forceScanDevices(value);
} catch (RemoteException e) {
e.printStackTrace();
return false;
}
setLanguageListTitle(value);
isSelectKeyboard = true;
return true;
}
@Override
public boolean onPreferenceTreeClick(
PreferenceScreen preferenceScreen, Preference preference) {
if (preference instanceof CheckBoxPreference) {
final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
Settings.System.putInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 1);
isSelectKeyboard = false;
if (chkPref.isChecked()) {
try {
SystemProperties.set("persist.sys.sa.lan", mSystemLanguage);
mWindowManager.forceScanDevices(mSystemLanguage);
} catch (RemoteException e) {
e.printStackTrace();
}
Settings.System.putInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 1);
mLanguageList.setEnabled(false);
setLanguageListTitle(mSystemLanguage);
} else {
Settings.System.putInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 0);
mLanguageList.setEnabled(true);
}
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
private PreferenceScreen createPreferenceHierarchy() {
// Root
final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
final Context context = getActivity();
final PreferenceCategory keyboardSettingsCategory = new PreferenceCategory(context);
root.addPreference(keyboardSettingsCategory);
keyboardSettingsCategory.setTitle("");
final CheckBoxPreference autoCB = new CheckBoxPreference(context);
mSubtypeAutoSelectionCBMap.put("autoCB", autoCB);
keyboardSettingsCategory.addPreference(autoCB);
autoCB.setTitle(R.string.use_system_language_to_select_input_method_subtypes);
final PreferenceCategory activeInputMethodsCategory = new PreferenceCategory(context);
activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes);
root.addPreference(activeInputMethodsCategory);
valLanguage = Settings.System.getString(getContentResolver(), Settings.System.EXTERNAL_KEYBOARD_VALUE);
mLanguageList = new ListPreference(context);
mLanguageList.setEntries(R.array.Keyboard_entries);
mLanguageList.setEntryValues(R.array.Keyboard_values);
CharSequence[] charLanguageKey = mLanguageList.getEntryValues();
CharSequence[] charLanguageEntries = mLanguageList.getEntries();
if (mKeyboardEntryMap.size() == 0) {
mKeyboardEntryMap.put("EnglishKey", default_language_keyboard);
for (int j=0; j<charLanguageEntries.length; j++) {
mKeyboardEntryMap.put((String)charLanguageKey[j], (String)charLanguageEntries[j]);
}
}
if (valLanguage == null) {
for (CharSequence c : charLanguageKey) {
if (mSystemLanguage.equals(c)) {
isSystemLanguage = true;
break;
}
isSystemLanguage = false;
}
mLanguageList.setValue(isSystemLanguage ? mSystemLanguage : default_language_value);
Settings.System.putInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 1);
} else {
for (CharSequence c : charLanguageKey) {
if (valLanguage.equals(c)) {
isSystemLanguage = true;
break;
}
isSystemLanguage = false;
}
mLanguageList.setValue(isSystemLanguage ? valLanguage : default_language_value);
isSelectKeyboard = true;
}
mLanguageList.setOnPreferenceChangeListener(this);
activeInputMethodsCategory.addPreference(mLanguageList);
int isCBCheck = Settings.System.getInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 1);
if (isCBCheck == 1) {
autoCB.setChecked(true);
} else {
autoCB.setChecked(false);
}
if (autoCB.isChecked()) {
mLanguageList.setEnabled(false);
} else {
mLanguageList.setEnabled(true);
}
return root;
}
@Override
public void onResume() {
super.onResume();
updateAutoSelectionCB();
}
private void updateAutoSelectionCB() {
int isCBCheck = Settings.System.getInt(getContentResolver(), Settings.System.USING_SYSTEM_LANGUAGE, 1);
if (isCBCheck == 1) {
setLanguageListTitle(mSystemLanguage);
mLanguageList.setValue(mSystemLanguage);
} else {
if (isSelectKeyboard) {
setLanguageListTitle(valLanguage);
}
}
}
private void setLanguageListTitle(String language) {
String v = mKeyboardEntryMap.get(language);
mLanguageList.setTitle((v == null ? mKeyboardEntryMap.get("EnglishKey") : v) + keyboard_name);
}
}