InputManager.java revision 9df6e7a926ce480baf70e97ee1b9ea387193f6ad
19df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown/* 29df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Copyright (C) 2012 The Android Open Source Project 39df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 49df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 59df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * you may not use this file except in compliance with the License. 69df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * You may obtain a copy of the License at 79df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 89df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * http://www.apache.org/licenses/LICENSE-2.0 99df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 109df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Unless required by applicable law or agreed to in writing, software 119df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 129df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * See the License for the specific language governing permissions and 149df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * limitations under the License. 159df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 169df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 179df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownpackage android.hardware.input; 189df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 199df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport com.android.internal.util.XmlUtils; 209df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 219df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.annotation.SdkConstant; 229df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.annotation.SdkConstant.SdkConstantType; 239df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.ComponentName; 249df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.Context; 259df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.Intent; 269df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.pm.ActivityInfo; 279df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.pm.PackageManager; 289df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.pm.ResolveInfo; 299df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 309df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.res.Resources; 319df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.res.TypedArray; 329df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.content.res.XmlResourceParser; 339df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.os.Bundle; 349df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.os.Parcel; 359df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.os.Parcelable; 369df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.util.Log; 379df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.view.KeyCharacterMap; 389df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport android.view.KeyCharacterMap.UnavailableException; 399df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 409df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport java.util.ArrayList; 419df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport java.util.HashMap; 429df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownimport java.util.List; 439df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 449df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown/** 459df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Provides information about input devices and available key layouts. 469df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <p> 479df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Get an instance of this class by calling 489df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link android.content.Context#getSystemService(java.lang.String) 499df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Context.getSystemService()} with the argument 509df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link android.content.Context#INPUT_SERVICE}. 519df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p> 529df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 539df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brownpublic final class InputManager { 549df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private static final String TAG = "InputManager"; 559df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 569df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private final Context mContext; 579df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 589df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown // Used to simulate a persistent data store. 599df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown // TODO: Replace with the real thing. 609df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private static final HashMap<String, String> mFakeRegistry = new HashMap<String, String>(); 619df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 629df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 639df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Broadcast Action: Query available keyboard layouts. 649df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <p> 659df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The input manager service locates available keyboard layouts 669df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * by querying broadcast receivers that are registered for this action. 679df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * An application can offer additional keyboard layouts to the user 689df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * by declaring a suitable broadcast receiver in its manifest. 699df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p><p> 709df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Here is an example broadcast receiver declaration that an application 719df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * might include in its AndroidManifest.xml to advertise keyboard layouts. 729df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The meta-data specifies a resource that contains a description of each keyboard 739df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * layout that is provided by the application. 749df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <pre><code> 759df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <receiver android:name=".InputDeviceReceiver"> 769df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <intent-filter> 779df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" /> 789df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </intent-filter> 799df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS" 809df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * android:resource="@xml/keyboard_layouts" /> 819df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </receiver> 829df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </code></pre> 839df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p><p> 849df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * In the above example, the <code>@xml/keyboard_layouts</code> resource refers to 859df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * an XML resource whose root element is <code><keyboard-layouts></code> that 869df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * contains zero or more <code><keyboard-layout></code> elements. 879df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Each <code><keyboard-layout></code> element specifies the name, label, and location 889df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * of a key character map for a particular keyboard layout. 899df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <pre></code> 909df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <?xml version="1.0" encoding="utf-8"?> 919df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android"> 929df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <keyboard-layout android:name="keyboard_layout_english_us" 939df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * android:label="@string/keyboard_layout_english_us_label" 949df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * android:kcm="@raw/keyboard_layout_english_us" /> 959df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </keyboard-layouts> 969df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p><p> 979df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The <code>android:name</code> attribute specifies an identifier by which 989df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * the keyboard layout will be known in the package. 999df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The <code>android:label</code> attributes specifies a human-readable descriptive 1009df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * label to describe the keyboard layout in the user interface, such as "English (US)". 1019df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The <code>android:kcm</code> attribute refers to a 1029df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <a href="http://source.android.com/tech/input/key-character-map-files.html"> 1039df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * key character map</a> resource that defines the keyboard layout. 1049df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p> 1059df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 1069df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1079df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public static final String ACTION_QUERY_KEYBOARD_LAYOUTS = 1089df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown "android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS"; 1099df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1109df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 1119df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Metadata Key: Keyboard layout metadata associated with 1129df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS}. 1139df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <p> 1149df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Specifies the resource id of a XML resource that describes the keyboard 1159df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * layouts that are provided by the application. 1169df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p> 1179df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 1189df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public static final String META_DATA_KEYBOARD_LAYOUTS = 1199df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown "android.hardware.input.metadata.KEYBOARD_LAYOUTS"; 1209df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1219df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** @hide */ 1229df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public InputManager(Context context) { 1239df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mContext = context; 1249df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1259df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1269df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 1279df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Gets information about all supported keyboard layouts. 1289df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <p> 1299df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * The input manager consults the built-in keyboard layouts as well 1309df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * as all keyboard layouts advertised by applications using a 1319df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link #ACTION_QUERY_KEYBOARD_LAYOUTS} broadcast receiver. 1329df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p> 1339df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 1349df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return A list of all supported keyboard layouts. 1359df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @hide 1369df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 1379df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public List<KeyboardLayout> getKeyboardLayouts() { 1389df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>(); 1399df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1409df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown final PackageManager pm = mContext.getPackageManager(); 1419df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Intent intent = new Intent(ACTION_QUERY_KEYBOARD_LAYOUTS); 1429df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown for (ResolveInfo resolveInfo : pm.queryBroadcastReceivers(intent, 1439df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown PackageManager.GET_META_DATA)) { 1449df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown loadKeyboardLayouts(pm, resolveInfo.activityInfo, list, null); 1459df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1469df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return list; 1479df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1489df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1499df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 1509df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Gets the keyboard layout with the specified descriptor. 1519df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 1529df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @param keyboardLayoutDescriptor The keyboard layout descriptor, as returned by 1539df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link KeyboardLayout#getDescriptor()}. 1549df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return The keyboard layout, or null if it could not be loaded. 1559df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 1569df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @hide 1579df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 1589df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { 1599df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (keyboardLayoutDescriptor == null) { 1609df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); 1619df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1629df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1639df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown KeyboardLayoutDescriptor d = parseKeyboardLayoutDescriptor(keyboardLayoutDescriptor); 1649df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (d == null) { 1659df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 1669df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1679df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1689df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown final PackageManager pm = mContext.getPackageManager(); 1699df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown try { 1709df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown ActivityInfo receiver = pm.getReceiverInfo( 1719df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown new ComponentName(d.packageName, d.receiverName), 1729df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown PackageManager.GET_META_DATA); 1739df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return loadKeyboardLayouts(pm, receiver, null, d.keyboardLayoutName); 1749df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } catch (NameNotFoundException ex) { 1759df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Could not load keyboard layout '" + d.keyboardLayoutName 1769df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' from receiver " + d.packageName + "/" + d.receiverName, ex); 1779df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 1789df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1799df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1809df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1819df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 1829df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Gets the keyboard layout descriptor for the specified input device. 1839df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 1849df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @param inputDeviceDescriptor The input device descriptor. 1859df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return The keyboard layout descriptor, or null if unknown or if the default 1869df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * keyboard layout will be used. 1879df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 1889df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @hide 1899df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 1909df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String getInputDeviceKeyboardLayoutDescriptor(String inputDeviceDescriptor) { 1919df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (inputDeviceDescriptor == null) { 1929df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 1939df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1949df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1959df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mFakeRegistry.get(inputDeviceDescriptor); 1969df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 1979df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 1989df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 1999df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Sets the keyboard layout descriptor for the specified input device. 2009df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * <p> 2019df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * This method may have the side-effect of causing the input device in question 2029df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * to be reconfigured. 2039df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * </p> 2049df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 2059df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @param inputDeviceDescriptor The input device descriptor. 2069df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @param keyboardLayoutDescriptor The keyboard layout descriptor, or null to remove 2079df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * the mapping so that the default keyboard layout will be used for the input device. 2089df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 2099df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @hide 2109df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 2119df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public void setInputDeviceKeyboardLayoutDescriptor(String inputDeviceDescriptor, 2129df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String keyboardLayoutDescriptor) { 2139df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (inputDeviceDescriptor == null) { 2149df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); 2159df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2169df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2179df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mFakeRegistry.put(inputDeviceDescriptor, keyboardLayoutDescriptor); 2189df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2199df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2209df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private KeyboardLayout loadKeyboardLayouts( 2219df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown PackageManager pm, ActivityInfo receiver, 2229df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown List<KeyboardLayout> list, String keyboardName) { 2239df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Bundle metaData = receiver.metaData; 2249df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (metaData == null) { 2259df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 2269df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2279df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2289df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown int configResId = metaData.getInt(META_DATA_KEYBOARD_LAYOUTS); 2299df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (configResId == 0) { 2309df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Missing meta-data '" + META_DATA_KEYBOARD_LAYOUTS + "' on receiver " 2319df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + receiver.packageName + "/" + receiver.name); 2329df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 2339df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2349df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2359df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown try { 2369df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Resources resources = pm.getResourcesForApplication(receiver.applicationInfo); 2379df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown XmlResourceParser parser = resources.getXml(configResId); 2389df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown try { 2399df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown XmlUtils.beginDocument(parser, "keyboard-layouts"); 2409df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2419df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown for (;;) { 2429df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown XmlUtils.nextElement(parser); 2439df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String element = parser.getName(); 2449df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (element == null) { 2459df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown break; 2469df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2479df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (element.equals("keyboard-layout")) { 2489df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown TypedArray a = resources.obtainAttributes( 2499df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown parser, com.android.internal.R.styleable.KeyboardLayout); 2509df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown try { 2519df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String name = a.getString( 2529df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown com.android.internal.R.styleable.KeyboardLayout_name); 2539df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String label = a.getString( 2549df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown com.android.internal.R.styleable.KeyboardLayout_label); 2559df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown int kcmResId = a.getResourceId( 2569df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown com.android.internal.R.styleable.KeyboardLayout_kcm, 0); 2579df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (name == null || label == null || kcmResId == 0) { 2589df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Missing required 'name', 'label' or 'kcm' " 2599df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "attributes in keyboard layout " 2609df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "resource from receiver " 2619df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + receiver.packageName + "/" + receiver.name); 2629df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } else { 2639df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String descriptor = makeKeyboardLayoutDescriptor( 2649df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown receiver.packageName, receiver.name, name); 2659df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown KeyboardLayout c = new KeyboardLayout( 2669df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown descriptor, label, kcmResId); 2679df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (keyboardName != null && name.equals(keyboardName)) { 2689df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return c; 2699df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2709df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (list != null) { 2719df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown list.add(c); 2729df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2739df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2749df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } finally { 2759df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown a.recycle(); 2769df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2779df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } else { 2789df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Skipping unrecognized element '" + element 2799df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' in keyboard layout resource from receiver " 2809df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + receiver.packageName + "/" + receiver.name); 2819df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2829df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2839df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } finally { 2849df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown parser.close(); 2859df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2869df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } catch (Exception ex) { 2879df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Could not load keyboard layout resource from receiver " 2889df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + receiver.packageName + "/" + receiver.name, ex); 2899df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 2909df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2919df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (keyboardName != null) { 2929df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.w(TAG, "Could not load keyboard layout '" + keyboardName 2939df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' from receiver " + receiver.packageName + "/" + receiver.name 2949df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + " because it was not declared in the keyboard layout resource."); 2959df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2969df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 2979df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 2989df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 2999df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private static String makeKeyboardLayoutDescriptor(String packageName, 3009df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String receiverName, String keyboardName) { 3019df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return packageName + "/" + receiverName + "/" + keyboardName; 3029df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3039df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3049df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private static KeyboardLayoutDescriptor parseKeyboardLayoutDescriptor(String descriptor) { 3059df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown int pos = descriptor.indexOf('/'); 3069df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (pos < 0 || pos + 1 == descriptor.length()) { 3079df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 3089df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3099df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown int pos2 = descriptor.indexOf('/', pos + 1); 3109df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (pos2 < pos + 2 || pos2 + 1 == descriptor.length()) { 3119df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 3129df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3139df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3149df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown KeyboardLayoutDescriptor result = new KeyboardLayoutDescriptor(); 3159df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown result.packageName = descriptor.substring(0, pos); 3169df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown result.receiverName = descriptor.substring(pos + 1, pos2); 3179df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown result.keyboardLayoutName = descriptor.substring(pos2 + 1); 3189df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return result; 3199df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3209df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3219df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 3229df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Describes a keyboard layout. 3239df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 3249df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @hide 3259df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 3269df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public static final class KeyboardLayout implements Parcelable, 3279df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Comparable<KeyboardLayout> { 3289df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private final String mDescriptor; 3299df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private final String mLabel; 3309df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private final int mKeyCharacterMapResId; 3319df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3329df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private KeyCharacterMap mKeyCharacterMap; 3339df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3349df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public static final Parcelable.Creator<KeyboardLayout> CREATOR = 3359df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown new Parcelable.Creator<KeyboardLayout>() { 3369df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public KeyboardLayout createFromParcel(Parcel source) { 3379df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return new KeyboardLayout(source); 3389df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3399df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public KeyboardLayout[] newArray(int size) { 3409df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return new KeyboardLayout[size]; 3419df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3429df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown }; 3439df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3449df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private KeyboardLayout(String descriptor, 3459df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown String label, int keyCharacterMapResId) { 3469df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mDescriptor = descriptor; 3479df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mLabel = label; 3489df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mKeyCharacterMapResId = keyCharacterMapResId; 3499df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3509df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3519df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private KeyboardLayout(Parcel source) { 3529df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mDescriptor = source.readString(); 3539df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mLabel = source.readString(); 3549df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mKeyCharacterMapResId = source.readInt(); 3559df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3569df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3579df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 3589df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Gets the keyboard layout descriptor, which can be used to retrieve 3599df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * the keyboard layout again later using 3609df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * {@link InputManager#getKeyboardLayout(String)}. 3619df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 3629df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return The keyboard layout descriptor. 3639df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 3649df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String getDescriptor() { 3659df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mDescriptor; 3669df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3679df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3689df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 3699df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Gets the keyboard layout descriptive label to show in the user interface. 3709df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return The keyboard layout descriptive label. 3719df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 3729df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String getLabel() { 3739df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mLabel; 3749df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3759df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3769df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown /** 3779df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * Loads the key character map associated with the keyboard layout. 3789df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * 3799df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @param pm The package manager. 3809df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown * @return The key character map, or null if it could not be loaded for any reason. 3819df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown */ 3829df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public KeyCharacterMap loadKeyCharacterMap(PackageManager pm) { 3839df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (pm == null) { 3849df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown throw new IllegalArgumentException("pm must not be null"); 3859df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3869df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3879df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (mKeyCharacterMap == null) { 3889df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown KeyboardLayoutDescriptor d = parseKeyboardLayoutDescriptor(mDescriptor); 3899df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (d == null) { 3909df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.e(TAG, "Could not load key character map '" + mDescriptor 3919df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' because the descriptor could not be parsed."); 3929df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 3939df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 3949df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 3959df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown CharSequence cs = pm.getText(d.packageName, mKeyCharacterMapResId, null); 3969df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown if (cs == null) { 3979df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.e(TAG, "Could not load key character map '" + mDescriptor 3989df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' because its associated resource could not be loaded."); 3999df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 4009df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4019df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4029df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown try { 4039df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown mKeyCharacterMap = KeyCharacterMap.load(cs); 4049df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } catch (UnavailableException ex) { 4059df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown Log.e(TAG, "Could not load key character map '" + mDescriptor 4069df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown + "' due to an error while parsing.", ex); 4079df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return null; 4089df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4099df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4109df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mKeyCharacterMap; 4119df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4129df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4139df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown @Override 4149df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public int describeContents() { 4159df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return 0; 4169df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4179df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4189df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown @Override 4199df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public void writeToParcel(Parcel dest, int flags) { 4209df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown dest.writeString(mDescriptor); 4219df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown dest.writeString(mLabel); 4229df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown dest.writeInt(mKeyCharacterMapResId); 4239df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4249df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4259df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown @Override 4269df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public int compareTo(KeyboardLayout another) { 4279df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mLabel.compareToIgnoreCase(another.mLabel); 4289df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4299df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4309df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown @Override 4319df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String toString() { 4329df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown return mLabel; 4339df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4349df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4359df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown 4369df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown private static final class KeyboardLayoutDescriptor { 4379df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String packageName; 4389df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String receiverName; 4399df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown public String keyboardLayoutName; 4409df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown } 4419df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown} 442