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     * &lt;receiver android:name=".InputDeviceReceiver">
769df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *     &lt;intent-filter>
779df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *         &lt;action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
789df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *     &lt;/intent-filter>
799df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *     &lt;meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS"
809df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *             android:resource="@xml/keyboard_layouts" />
819df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     * &lt;/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>&lt;keyboard-layouts></code> that
869df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     * contains zero or more <code>&lt;keyboard-layout></code> elements.
879df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     * Each <code>&lt;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     * &lt;?xml version="1.0" encoding="utf-8"?>
919df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     * &lt;keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
929df6e7a926ce480baf70e97ee1b9ea387193f6adJeff Brown     *     &lt;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     * &lt;/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