diff options
-rw-r--r-- | core/java/android/hardware/input/IInputManager.aidl | 9 | ||||
-rwxr-xr-x | core/java/android/hardware/input/InputManager.java | 140 | ||||
-rw-r--r-- | core/java/android/view/WindowManagerPolicy.java | 6 | ||||
-rwxr-xr-x | core/res/res/drawable-hdpi/ic_settings_language.png | bin | 0 -> 986 bytes | |||
-rw-r--r-- | core/res/res/drawable-mdpi/ic_settings_language.png | bin | 0 -> 756 bytes | |||
-rw-r--r-- | core/res/res/drawable-xhdpi/ic_settings_language.png | bin | 0 -> 1267 bytes | |||
-rw-r--r-- | core/res/res/values/public.xml | 3 | ||||
-rwxr-xr-x | core/res/res/values/strings.xml | 5 | ||||
-rw-r--r-- | data/keyboards/Generic.kcm | 1 | ||||
-rw-r--r-- | data/keyboards/Virtual.kcm | 1 | ||||
-rwxr-xr-x | policy/src/com/android/internal/policy/impl/PhoneWindowManager.java | 17 | ||||
-rw-r--r-- | services/java/com/android/server/input/InputManagerService.java | 496 | ||||
-rw-r--r-- | services/java/com/android/server/input/PersistentDataStore.java | 416 | ||||
-rwxr-xr-x | services/java/com/android/server/wm/WindowManagerService.java | 6 |
14 files changed, 869 insertions, 231 deletions
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 3137947..9b6f82a 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -41,8 +41,13 @@ interface IInputManager { // Keyboard layouts configuration. KeyboardLayout[] getKeyboardLayouts(); KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor); - String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor); - void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor); + void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor); + String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor); + void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor); + void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor); // Registers an input devices changed listener. diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index dfd35e1..262d87d 100755 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -16,6 +16,8 @@ package android.hardware.input; +import com.android.internal.util.ArrayUtils; + import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; @@ -217,6 +219,41 @@ public final class InputManager { } /** + * Gets information about the input device with the specified descriptor. + * @param descriptor The input device descriptor. + * @return The input device or null if not found. + * @hide + */ + public InputDevice getInputDeviceByDescriptor(String descriptor) { + if (descriptor == null) { + throw new IllegalArgumentException("descriptor must not be null."); + } + + synchronized (mInputDevicesLock) { + populateInputDevicesLocked(); + + int numDevices = mInputDevices.size(); + for (int i = 0; i < numDevices; i++) { + InputDevice inputDevice = mInputDevices.valueAt(i); + if (inputDevice == null) { + int id = mInputDevices.keyAt(i); + try { + inputDevice = mIm.getInputDevice(id); + } catch (RemoteException ex) { + // Ignore the problem for the purposes of this method. + continue; + } + mInputDevices.setValueAt(i, inputDevice); + } + if (descriptor.equals(inputDevice.getDescriptor())) { + return inputDevice; + } + } + return null; + } + } + + /** * Gets the ids of all input devices in the system. * @return The input device ids. */ @@ -332,50 +369,129 @@ public final class InputManager { } /** - * Gets the keyboard layout descriptor for the specified input device. + * Gets the current keyboard layout descriptor for the specified input device. * * @param inputDeviceDescriptor The input device descriptor. - * @return The keyboard layout descriptor, or null if unknown or if the default - * keyboard layout will be used. + * @return The keyboard layout descriptor, or null if no keyboard layout has been set. * * @hide */ - public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { + public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } try { - return mIm.getKeyboardLayoutForInputDevice(inputDeviceDescriptor); + return mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor); } catch (RemoteException ex) { - Log.w(TAG, "Could not get keyboard layout for input device.", ex); + Log.w(TAG, "Could not get current keyboard layout for input device.", ex); return null; } } /** - * Sets the keyboard layout descriptor for the specified input device. + * Sets the current keyboard layout descriptor for the specified input device. + * <p> + * This method may have the side-effect of causing the input device in question + * to be reconfigured. + * </p> + * + * @param inputDeviceDescriptor The input device descriptor. + * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null. + * + * @hide + */ + public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } + + try { + mIm.setCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor, + keyboardLayoutDescriptor); + } catch (RemoteException ex) { + Log.w(TAG, "Could not set current keyboard layout for input device.", ex); + } + } + + /** + * Gets all keyboard layout descriptors that are enabled for the specified input device. + * + * @param inputDeviceDescriptor The input device descriptor. + * @return The keyboard layout descriptors. + * + * @hide + */ + public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) { + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + + try { + return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor); + } catch (RemoteException ex) { + Log.w(TAG, "Could not get keyboard layouts for input device.", ex); + return ArrayUtils.emptyArray(String.class); + } + } + + /** + * Adds the keyboard layout descriptor for the specified input device. * <p> * This method may have the side-effect of causing the input device in question * to be reconfigured. * </p> * * @param inputDeviceDescriptor The input device descriptor. - * @param keyboardLayoutDescriptor The keyboard layout descriptor, or null to remove - * the mapping so that the default keyboard layout will be used for the input device. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to add. * * @hide */ - public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor) { if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } + + try { + mIm.addKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); + } catch (RemoteException ex) { + Log.w(TAG, "Could not add keyboard layout for input device.", ex); + } + } + + /** + * Removes the keyboard layout descriptor for the specified input device. + * <p> + * This method may have the side-effect of causing the input device in question + * to be reconfigured. + * </p> + * + * @param inputDeviceDescriptor The input device descriptor. + * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to remove. + * + * @hide + */ + public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } try { - mIm.setKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); + mIm.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor); } catch (RemoteException ex) { - Log.w(TAG, "Could not set keyboard layout for input device.", ex); + Log.w(TAG, "Could not remove keyboard layout for input device.", ex); } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 0c5d6ea..ceb9fe6 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -386,6 +386,12 @@ public interface WindowManagerPolicy { */ public InputChannel monitorInput(String name); + /** + * Switch the keyboard layout for the given device. + * Direction should be +1 or -1 to go to the next or previous keyboard layout. + */ + public void switchKeyboardLayout(int deviceId, int direction); + public void shutdown(); public void rebootSafeMode(); } diff --git a/core/res/res/drawable-hdpi/ic_settings_language.png b/core/res/res/drawable-hdpi/ic_settings_language.png Binary files differnew file mode 100755 index 0000000..f635b2e --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_settings_language.png diff --git a/core/res/res/drawable-mdpi/ic_settings_language.png b/core/res/res/drawable-mdpi/ic_settings_language.png Binary files differnew file mode 100644 index 0000000..f8aca67 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_settings_language.png diff --git a/core/res/res/drawable-xhdpi/ic_settings_language.png b/core/res/res/drawable-xhdpi/ic_settings_language.png Binary files differnew file mode 100644 index 0000000..2c42db3 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_settings_language.png diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a24e345..83d3141 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1489,6 +1489,8 @@ <java-symbol type="string" name="low_internal_storage_view_title" /> <java-symbol type="string" name="report" /> <java-symbol type="string" name="select_input_method" /> + <java-symbol type="string" name="select_keyboard_layout_notification_title" /> + <java-symbol type="string" name="select_keyboard_layout_notification_message" /> <java-symbol type="string" name="smv_application" /> <java-symbol type="string" name="smv_process" /> <java-symbol type="string" name="tethered_notification_message" /> @@ -1580,6 +1582,7 @@ <java-symbol type="drawable" name="expander_ic_minimized" /> <java-symbol type="drawable" name="ic_menu_archive" /> <java-symbol type="drawable" name="ic_menu_goto" /> + <java-symbol type="drawable" name="ic_settings_language" /> <java-symbol type="drawable" name="title_bar_medium" /> <java-symbol type="id" name="body" /> <java-symbol type="string" name="fast_scroll_alphabet" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 38ce854..d50e2de 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3092,6 +3092,11 @@ <!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=10] --> <string name="hardware">Hardware</string> + <!-- Title of the notification to prompt the user to select a keyboard layout. --> + <string name="select_keyboard_layout_notification_title">Select keyboard layout</string> + <!-- Message of the notification to prompt the user to select a keyboard layout. --> + <string name="select_keyboard_layout_notification_message">Touch to select a keyboard layout.</string> + <string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> <string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string> diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm index 544076f..782a701 100644 --- a/data/keyboards/Generic.kcm +++ b/data/keyboards/Generic.kcm @@ -252,6 +252,7 @@ key SPACE { label: ' ' base: ' ' alt, meta: fallback SEARCH + ctrl: fallback LANGUAGE_SWITCH } key ENTER { diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm index e592013..d90b790 100644 --- a/data/keyboards/Virtual.kcm +++ b/data/keyboards/Virtual.kcm @@ -249,6 +249,7 @@ key SPACE { label: ' ' base: ' ' alt, meta: fallback SEARCH + ctrl: fallback LANGUAGE_SWITCH } key ENTER { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index cee01ac..29de5c1 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -326,6 +326,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { RecentApplicationsDialog mRecentAppsDialog; int mRecentAppsDialogHeldModifiers; + boolean mLanguageSwitchKeyPressed; int mLidState = LID_ABSENT; boolean mHaveBuiltInKeyboard; @@ -1943,6 +1944,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH); } + // Handle keyboard language switching. + if (down && repeatCount == 0 + && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH + || (keyCode == KeyEvent.KEYCODE_SPACE + && (metaState & KeyEvent.META_CTRL_MASK) != 0))) { + int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; + mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + return -1; + } + if (mLanguageSwitchKeyPressed && !down + && (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH + || keyCode == KeyEvent.KEYCODE_SPACE)) { + mLanguageSwitchKeyPressed = false; + return -1; + } + // Let the application handle the key. return 0; } diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java index 4f1f76f..bc946b5 100644 --- a/services/java/com/android/server/input/InputManagerService.java +++ b/services/java/com/android/server/input/InputManagerService.java @@ -16,8 +16,7 @@ package com.android.server.input; -import com.android.internal.os.AtomicFile; -import com.android.internal.util.FastXmlSerializer; +import com.android.internal.R; import com.android.internal.util.XmlUtils; import com.android.server.Watchdog; @@ -26,6 +25,9 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.Manifest; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; @@ -70,23 +72,20 @@ import android.view.PointerIcon; import android.view.Surface; import android.view.ViewConfiguration; import android.view.WindowManagerPolicy; +import android.widget.Toast; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; -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.Collections; import java.util.HashMap; -import java.util.Map; +import java.util.HashSet; -import libcore.io.IoUtils; import libcore.io.Streams; import libcore.util.Objects; @@ -100,6 +99,10 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml"; private static final int MSG_DELIVER_INPUT_DEVICES_CHANGED = 1; + private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 2; + private static final int MSG_RELOAD_KEYBOARD_LAYOUTS = 3; + private static final int MSG_UPDATE_KEYBOARD_LAYOUTS = 4; + private static final int MSG_RELOAD_DEVICE_ALIASES = 5; // Pointer to native input manager service object. private final int mPtr; @@ -109,6 +112,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private final InputManagerHandler mHandler; private boolean mSystemReady; private BluetoothService mBluetoothService; + private NotificationManager mNotificationManager; // Persistent data store. Must be locked each time during use. private final PersistentDataStore mDataStore = new PersistentDataStore(); @@ -122,6 +126,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. private final ArrayList<InputDevicesChangedListenerRecord> mTempInputDevicesChangedListenersToNotify = new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only + private final ArrayList<InputDevice> + mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only + private boolean mKeyboardLayoutNotificationShown; + private PendingIntent mKeyboardLayoutIntent; + private Toast mSwitchedKeyboardLayoutToast; // State for vibrator tokens. private Object mVibratorLock = new Object(); @@ -235,6 +244,8 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. Slog.d(TAG, "System ready."); } mBluetoothService = bluetoothService; + mNotificationManager = (NotificationManager)mContext.getSystemService( + Context.NOTIFICATION_SERVICE); mSystemReady = true; IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); @@ -244,10 +255,7 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Slog.d(TAG, "Packages changed, reloading keyboard layouts."); - } - reloadKeyboardLayouts(); + updateKeyboardLayouts(); } }, filter, null, mHandler); @@ -255,22 +263,25 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Slog.d(TAG, "Bluetooth alias changed, reloading device names."); - } reloadDeviceAliases(); } }, filter, null, mHandler); - reloadKeyboardLayouts(); - reloadDeviceAliases(); + mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES); + mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS); } private void reloadKeyboardLayouts() { + if (DEBUG) { + Slog.d(TAG, "Reloading keyboard layouts."); + } nativeReloadKeyboardLayouts(mPtr); } private void reloadDeviceAliases() { + if (DEBUG) { + Slog.d(TAG, "Reloading device names."); + } nativeReloadDeviceAliases(mPtr); } @@ -558,9 +569,11 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. } // Must be called on handler. - private void deliverInputDevicesChanged() { + private void deliverInputDevicesChanged(InputDevice[] oldInputDevices) { + // Scan for changes. + int numFullKeyboardsAdded = 0; mTempInputDevicesChangedListenersToNotify.clear(); - + mTempFullKeyboards.clear(); final int numListeners; final int[] deviceIdAndGeneration; synchronized (mInputDevicesLock) { @@ -581,13 +594,126 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. final InputDevice inputDevice = mInputDevices[i]; deviceIdAndGeneration[i * 2] = inputDevice.getId(); deviceIdAndGeneration[i * 2 + 1] = inputDevice.getGeneration(); + + if (isFullKeyboard(inputDevice)) { + if (!containsInputDeviceWithDescriptor(oldInputDevices, + inputDevice.getDescriptor())) { + mTempFullKeyboards.add(numFullKeyboardsAdded++, inputDevice); + } else { + mTempFullKeyboards.add(inputDevice); + } + } } } + // Notify listeners. for (int i = 0; i < numListeners; i++) { mTempInputDevicesChangedListenersToNotify.get(i).notifyInputDevicesChanged( deviceIdAndGeneration); } + mTempInputDevicesChangedListenersToNotify.clear(); + + // Check for missing keyboard layouts. + if (mNotificationManager != null) { + final int numFullKeyboards = mTempFullKeyboards.size(); + boolean missingLayoutForExternalKeyboard = false; + boolean missingLayoutForExternalKeyboardAdded = false; + synchronized (mDataStore) { + for (int i = 0; i < numFullKeyboards; i++) { + final InputDevice inputDevice = mTempFullKeyboards.get(i); + if (mDataStore.getCurrentKeyboardLayout(inputDevice.getDescriptor()) == null) { + missingLayoutForExternalKeyboard = true; + if (i < numFullKeyboardsAdded) { + missingLayoutForExternalKeyboardAdded = true; + } + } + } + } + if (missingLayoutForExternalKeyboard) { + if (missingLayoutForExternalKeyboardAdded) { + showMissingKeyboardLayoutNotification(); + } + } else if (mKeyboardLayoutNotificationShown) { + hideMissingKeyboardLayoutNotification(); + } + } + mTempFullKeyboards.clear(); + } + + // Must be called on handler. + private void showMissingKeyboardLayoutNotification() { + if (!mKeyboardLayoutNotificationShown) { + if (mKeyboardLayoutIntent == null) { + final Intent intent = new Intent("android.settings.INPUT_METHOD_SETTINGS"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mKeyboardLayoutIntent = PendingIntent.getActivity(mContext, 0, intent, 0); + } + + Resources r = mContext.getResources(); + Notification notification = new Notification.Builder(mContext) + .setContentTitle(r.getString( + R.string.select_keyboard_layout_notification_title)) + .setContentText(r.getString( + R.string.select_keyboard_layout_notification_message)) + .setContentIntent(mKeyboardLayoutIntent) + .setSmallIcon(R.drawable.ic_settings_language) + .setPriority(Notification.PRIORITY_LOW) + .build(); + mNotificationManager.notify(R.string.select_keyboard_layout_notification_title, + notification); + mKeyboardLayoutNotificationShown = true; + } + } + + // Must be called on handler. + private void hideMissingKeyboardLayoutNotification() { + if (mKeyboardLayoutNotificationShown) { + mKeyboardLayoutNotificationShown = false; + mNotificationManager.cancel(R.string.select_keyboard_layout_notification_title); + } + } + + // Must be called on handler. + private void updateKeyboardLayouts() { + // Scan all input devices state for keyboard layouts that have been uninstalled. + final HashSet<String> availableKeyboardLayouts = new HashSet<String>(); + visitAllKeyboardLayouts(new KeyboardLayoutVisitor() { + @Override + public void visitKeyboardLayout(Resources resources, + String descriptor, String label, String collection, int keyboardLayoutResId) { + availableKeyboardLayouts.add(descriptor); + } + }); + synchronized (mDataStore) { + try { + mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts); + } finally { + mDataStore.saveIfNeeded(); + } + } + + // Reload keyboard layouts. + reloadKeyboardLayouts(); + } + + private static boolean isFullKeyboard(InputDevice inputDevice) { + return !inputDevice.isVirtual() + && (inputDevice.getSources() & InputDevice.SOURCE_KEYBOARD) != 0 + && inputDevice.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC; + } + + private static boolean containsInputDeviceWithDescriptor(InputDevice[] inputDevices, + String descriptor) { + final int numDevices = inputDevices.length; + for (int i = 0; i < numDevices; i++) { + final InputDevice inputDevice = inputDevices[i]; + if (inputDevice.getDescriptor().equals(descriptor)) { + return true; + } + } + return false; } @Override // Binder call @@ -720,43 +846,147 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. } @Override // Binder call - public String getKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { + public String getCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor) { if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } synchronized (mDataStore) { - return mDataStore.getKeyboardLayout(inputDeviceDescriptor); + return mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor); } } @Override // Binder call - public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + public void setCurrentKeyboardLayoutForInputDevice(String inputDeviceDescriptor, String keyboardLayoutDescriptor) { if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT, - "setKeyboardLayoutForInputDevice()")) { + "setCurrentKeyboardLayoutForInputDevice()")) { throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission"); } + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } + + synchronized (mDataStore) { + try { + if (mDataStore.setCurrentKeyboardLayout( + inputDeviceDescriptor, keyboardLayoutDescriptor)) { + mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); + } + } finally { + mDataStore.saveIfNeeded(); + } + } + } + @Override // Binder call + public String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) { if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); } - final boolean changed; + synchronized (mDataStore) { + return mDataStore.getKeyboardLayouts(inputDeviceDescriptor); + } + } + + @Override // Binder call + public void addKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT, + "addKeyboardLayoutForInputDevice()")) { + throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission"); + } + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } + synchronized (mDataStore) { try { - changed = mDataStore.setKeyboardLayout( - inputDeviceDescriptor, keyboardLayoutDescriptor); + String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor); + if (mDataStore.addKeyboardLayout(inputDeviceDescriptor, keyboardLayoutDescriptor) + && !Objects.equal(oldLayout, + mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) { + mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); + } } finally { mDataStore.saveIfNeeded(); } } + } - if (changed) { - if (DEBUG) { - Slog.d(TAG, "Keyboard layout changed, reloading keyboard layouts."); + @Override // Binder call + public void removeKeyboardLayoutForInputDevice(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT, + "removeKeyboardLayoutForInputDevice()")) { + throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission"); + } + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException("inputDeviceDescriptor must not be null"); + } + if (keyboardLayoutDescriptor == null) { + throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null"); + } + + synchronized (mDataStore) { + try { + String oldLayout = mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor); + if (mDataStore.removeKeyboardLayout(inputDeviceDescriptor, + keyboardLayoutDescriptor) + && !Objects.equal(oldLayout, + mDataStore.getCurrentKeyboardLayout(inputDeviceDescriptor))) { + mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); + } + } finally { + mDataStore.saveIfNeeded(); + } + } + } + + public void switchKeyboardLayout(int deviceId, int direction) { + mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget(); + } + + // Must be called on handler. + private void handleSwitchKeyboardLayout(int deviceId, int direction) { + final InputDevice device = getInputDevice(deviceId); + final String inputDeviceDescriptor = device.getDescriptor(); + if (device != null) { + final boolean changed; + final String keyboardLayoutDescriptor; + synchronized (mDataStore) { + try { + changed = mDataStore.switchKeyboardLayout(inputDeviceDescriptor, direction); + keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout( + inputDeviceDescriptor); + } finally { + mDataStore.saveIfNeeded(); + } + } + + if (changed) { + if (mSwitchedKeyboardLayoutToast != null) { + mSwitchedKeyboardLayoutToast.cancel(); + mSwitchedKeyboardLayoutToast = null; + } + if (keyboardLayoutDescriptor != null) { + KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor); + if (keyboardLayout != null) { + mSwitchedKeyboardLayoutToast = Toast.makeText( + mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT); + mSwitchedKeyboardLayoutToast.show(); + } + } + + reloadKeyboardLayouts(); } - reloadKeyboardLayouts(); } } @@ -978,12 +1208,13 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. // Native callback. private void notifyInputDevicesChanged(InputDevice[] inputDevices) { synchronized (mInputDevicesLock) { - mInputDevices = inputDevices; - if (!mInputDevicesChangedPending) { mInputDevicesChangedPending = true; - mHandler.sendEmptyMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED); + mHandler.obtainMessage(MSG_DELIVER_INPUT_DEVICES_CHANGED, + mInputDevices).sendToTarget(); } + + mInputDevices = inputDevices; } } @@ -1132,7 +1363,8 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. return null; } - String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor); + String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice( + inputDeviceDescriptor); if (keyboardLayoutDescriptor == null) { return null; } @@ -1203,7 +1435,19 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. public void handleMessage(Message msg) { switch (msg.what) { case MSG_DELIVER_INPUT_DEVICES_CHANGED: - deliverInputDevicesChanged(); + deliverInputDevicesChanged((InputDevice[])msg.obj); + break; + case MSG_SWITCH_KEYBOARD_LAYOUT: + handleSwitchKeyboardLayout(msg.arg1, msg.arg2); + break; + case MSG_RELOAD_KEYBOARD_LAYOUTS: + reloadKeyboardLayouts(); + break; + case MSG_UPDATE_KEYBOARD_LAYOUTS: + updateKeyboardLayouts(); + break; + case MSG_RELOAD_DEVICE_ALIASES: + reloadDeviceAliases(); break; } } @@ -1316,186 +1560,4 @@ public class InputManagerService extends IInputManager.Stub implements Watchdog. onVibratorTokenDied(this); } } - - /** - * Manages persistent state recorded by the input manager service as an XML file. - * Caller must acquire lock on the data store before accessing it. - * - * File format: - * <code> - * <input-mananger-state> - * <input-devices> - * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" /> - * >input-devices> - * >/input-manager-state> - * </code> - */ - private static final class PersistentDataStore { - // Input device state by descriptor. - private final HashMap<String, InputDeviceState> mInputDevices = - new HashMap<String, InputDeviceState>(); - private final AtomicFile mAtomicFile; - - // True if the data has been loaded. - private boolean mLoaded; - - // True if there are changes to be saved. - private boolean mDirty; - - public PersistentDataStore() { - mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml")); - } - - public void saveIfNeeded() { - if (mDirty) { - save(); - mDirty = false; - } - } - - public String getKeyboardLayout(String inputDeviceDescriptor) { - InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); - return state != null ? state.keyboardLayoutDescriptor : null; - } - - public boolean setKeyboardLayout(String inputDeviceDescriptor, - String keyboardLayoutDescriptor) { - InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); - if (!Objects.equal(state.keyboardLayoutDescriptor, keyboardLayoutDescriptor)) { - state.keyboardLayoutDescriptor = keyboardLayoutDescriptor; - setDirty(); - return true; - } - return false; - } - - private InputDeviceState getInputDeviceState(String inputDeviceDescriptor, - boolean createIfAbsent) { - loadIfNeeded(); - InputDeviceState state = mInputDevices.get(inputDeviceDescriptor); - if (state == null && createIfAbsent) { - state = new InputDeviceState(); - mInputDevices.put(inputDeviceDescriptor, state); - setDirty(); - } - return state; - } - - private void loadIfNeeded() { - if (!mLoaded) { - load(); - mLoaded = true; - } - } - - private void setDirty() { - mDirty = true; - } - - private void clearState() { - mInputDevices.clear(); - } - - private void load() { - clearState(); - - final InputStream is; - try { - is = mAtomicFile.openRead(); - } catch (FileNotFoundException ex) { - return; - } - - XmlPullParser parser; - try { - parser = Xml.newPullParser(); - parser.setInput(new BufferedInputStream(is), null); - loadFromXml(parser); - } catch (IOException ex) { - Slog.w(TAG, "Failed to load input manager persistent store data.", ex); - clearState(); - } catch (XmlPullParserException ex) { - Slog.w(TAG, "Failed to load input manager persistent store data.", ex); - clearState(); - } finally { - IoUtils.closeQuietly(is); - } - } - - private void save() { - final FileOutputStream os; - try { - os = mAtomicFile.startWrite(); - boolean success = false; - try { - XmlSerializer serializer = new FastXmlSerializer(); - serializer.setOutput(new BufferedOutputStream(os), "utf-8"); - saveToXml(serializer); - serializer.flush(); - success = true; - } finally { - if (success) { - mAtomicFile.finishWrite(os); - } else { - mAtomicFile.failWrite(os); - } - } - } catch (IOException ex) { - Slog.w(TAG, "Failed to save input manager persistent store data.", ex); - } - } - - private void loadFromXml(XmlPullParser parser) - throws IOException, XmlPullParserException { - XmlUtils.beginDocument(parser, "input-manager-state"); - final int outerDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("input-devices")) { - loadInputDevicesFromXml(parser); - } - } - } - - private void loadInputDevicesFromXml(XmlPullParser parser) - throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals("input-device")) { - String descriptor = parser.getAttributeValue(null, "descriptor"); - if (descriptor == null) { - throw new XmlPullParserException( - "Missing descriptor attribute on input-device"); - } - InputDeviceState state = new InputDeviceState(); - state.keyboardLayoutDescriptor = - parser.getAttributeValue(null, "keyboard-layout"); - mInputDevices.put(descriptor, state); - } - } - } - - private void saveToXml(XmlSerializer serializer) throws IOException { - serializer.startDocument(null, true); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startTag(null, "input-manager-state"); - serializer.startTag(null, "input-devices"); - for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) { - final String descriptor = entry.getKey(); - final InputDeviceState state = entry.getValue(); - serializer.startTag(null, "input-device"); - serializer.attribute(null, "descriptor", descriptor); - if (state.keyboardLayoutDescriptor != null) { - serializer.attribute(null, "keyboard-layout", state.keyboardLayoutDescriptor); - } - serializer.endTag(null, "input-device"); - } - serializer.endTag(null, "input-devices"); - serializer.endTag(null, "input-manager-state"); - serializer.endDocument(); - } - } - - private static final class InputDeviceState { - public String keyboardLayoutDescriptor; - } } diff --git a/services/java/com/android/server/input/PersistentDataStore.java b/services/java/com/android/server/input/PersistentDataStore.java new file mode 100644 index 0000000..fbe3e8b --- /dev/null +++ b/services/java/com/android/server/input/PersistentDataStore.java @@ -0,0 +1,416 @@ +/* + * 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. + */ + +package com.android.server.input; + +import com.android.internal.os.AtomicFile; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.util.Slog; +import android.util.Xml; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import libcore.io.IoUtils; +import libcore.util.Objects; + +/** + * Manages persistent state recorded by the input manager service as an XML file. + * Caller must acquire lock on the data store before accessing it. + * + * File format: + * <code> + * <input-mananger-state> + * <input-devices> + * <input-device descriptor="xxxxx" keyboard-layout="yyyyy" /> + * >input-devices> + * >/input-manager-state> + * </code> + */ +final class PersistentDataStore { + static final String TAG = "InputManager"; + + // Input device state by descriptor. + private final HashMap<String, InputDeviceState> mInputDevices = + new HashMap<String, InputDeviceState>(); + private final AtomicFile mAtomicFile; + + // True if the data has been loaded. + private boolean mLoaded; + + // True if there are changes to be saved. + private boolean mDirty; + + public PersistentDataStore() { + mAtomicFile = new AtomicFile(new File("/data/system/input-manager-state.xml")); + } + + public void saveIfNeeded() { + if (mDirty) { + save(); + mDirty = false; + } + } + + public String getCurrentKeyboardLayout(String inputDeviceDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); + return state != null ? state.getCurrentKeyboardLayout() : null; + } + + public boolean setCurrentKeyboardLayout(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); + if (state.setCurrentKeyboardLayout(keyboardLayoutDescriptor)) { + setDirty(); + return true; + } + return false; + } + + public String[] getKeyboardLayouts(String inputDeviceDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); + if (state == null) { + return (String[])ArrayUtils.emptyArray(String.class); + } + return state.getKeyboardLayouts(); + } + + public boolean addKeyboardLayout(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); + if (state.addKeyboardLayout(keyboardLayoutDescriptor)) { + setDirty(); + return true; + } + return false; + } + + public boolean removeKeyboardLayout(String inputDeviceDescriptor, + String keyboardLayoutDescriptor) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true); + if (state.removeKeyboardLayout(keyboardLayoutDescriptor)) { + setDirty(); + return true; + } + return false; + } + + public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) { + InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false); + if (state != null && state.switchKeyboardLayout(direction)) { + setDirty(); + return true; + } + return false; + } + + public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) { + boolean changed = false; + for (InputDeviceState state : mInputDevices.values()) { + if (state.removeUninstalledKeyboardLayouts(availableKeyboardLayouts)) { + changed = true; + } + } + if (changed) { + setDirty(); + return true; + } + return false; + } + + private InputDeviceState getInputDeviceState(String inputDeviceDescriptor, + boolean createIfAbsent) { + loadIfNeeded(); + InputDeviceState state = mInputDevices.get(inputDeviceDescriptor); + if (state == null && createIfAbsent) { + state = new InputDeviceState(); + mInputDevices.put(inputDeviceDescriptor, state); + setDirty(); + } + return state; + } + + private void loadIfNeeded() { + if (!mLoaded) { + load(); + mLoaded = true; + } + } + + private void setDirty() { + mDirty = true; + } + + private void clearState() { + mInputDevices.clear(); + } + + private void load() { + clearState(); + + final InputStream is; + try { + is = mAtomicFile.openRead(); + } catch (FileNotFoundException ex) { + return; + } + + XmlPullParser parser; + try { + parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(is), null); + loadFromXml(parser); + } catch (IOException ex) { + Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex); + clearState(); + } catch (XmlPullParserException ex) { + Slog.w(InputManagerService.TAG, "Failed to load input manager persistent store data.", ex); + clearState(); + } finally { + IoUtils.closeQuietly(is); + } + } + + private void save() { + final FileOutputStream os; + try { + os = mAtomicFile.startWrite(); + boolean success = false; + try { + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(new BufferedOutputStream(os), "utf-8"); + saveToXml(serializer); + serializer.flush(); + success = true; + } finally { + if (success) { + mAtomicFile.finishWrite(os); + } else { + mAtomicFile.failWrite(os); + } + } + } catch (IOException ex) { + Slog.w(InputManagerService.TAG, "Failed to save input manager persistent store data.", ex); + } + } + + private void loadFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + XmlUtils.beginDocument(parser, "input-manager-state"); + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("input-devices")) { + loadInputDevicesFromXml(parser); + } + } + } + + private void loadInputDevicesFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("input-device")) { + String descriptor = parser.getAttributeValue(null, "descriptor"); + if (descriptor == null) { + throw new XmlPullParserException( + "Missing descriptor attribute on input-device."); + } + if (mInputDevices.containsKey(descriptor)) { + throw new XmlPullParserException("Found duplicate input device."); + } + + InputDeviceState state = new InputDeviceState(); + state.loadFromXml(parser); + mInputDevices.put(descriptor, state); + } + } + } + + private void saveToXml(XmlSerializer serializer) throws IOException { + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startTag(null, "input-manager-state"); + serializer.startTag(null, "input-devices"); + for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) { + final String descriptor = entry.getKey(); + final InputDeviceState state = entry.getValue(); + serializer.startTag(null, "input-device"); + serializer.attribute(null, "descriptor", descriptor); + state.saveToXml(serializer); + serializer.endTag(null, "input-device"); + } + serializer.endTag(null, "input-devices"); + serializer.endTag(null, "input-manager-state"); + serializer.endDocument(); + } + + private static final class InputDeviceState { + private String mCurrentKeyboardLayout; + private ArrayList<String> mKeyboardLayouts = new ArrayList<String>(); + + public String getCurrentKeyboardLayout() { + return mCurrentKeyboardLayout; + } + + public boolean setCurrentKeyboardLayout(String keyboardLayout) { + if (Objects.equal(mCurrentKeyboardLayout, keyboardLayout)) { + return false; + } + addKeyboardLayout(keyboardLayout); + mCurrentKeyboardLayout = keyboardLayout; + return true; + } + + public String[] getKeyboardLayouts() { + if (mKeyboardLayouts.isEmpty()) { + return (String[])ArrayUtils.emptyArray(String.class); + } + return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]); + } + + public boolean addKeyboardLayout(String keyboardLayout) { + int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout); + if (index >= 0) { + return false; + } + mKeyboardLayouts.add(-index - 1, keyboardLayout); + if (mCurrentKeyboardLayout == null) { + mCurrentKeyboardLayout = keyboardLayout; + } + return true; + } + + public boolean removeKeyboardLayout(String keyboardLayout) { + int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout); + if (index < 0) { + return false; + } + mKeyboardLayouts.remove(index); + updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index); + return true; + } + + private void updateCurrentKeyboardLayoutIfRemoved( + String removedKeyboardLayout, int removedIndex) { + if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) { + if (!mKeyboardLayouts.isEmpty()) { + int index = removedIndex; + if (index == mKeyboardLayouts.size()) { + index = 0; + } + mCurrentKeyboardLayout = mKeyboardLayouts.get(index); + } else { + mCurrentKeyboardLayout = null; + } + } + } + + public boolean switchKeyboardLayout(int direction) { + final int size = mKeyboardLayouts.size(); + if (size < 2) { + return false; + } + int index = Collections.binarySearch(mKeyboardLayouts, mCurrentKeyboardLayout); + assert index >= 0; + if (direction > 0) { + index = (index + 1) % size; + } else { + index = (index + size - 1) % size; + } + mCurrentKeyboardLayout = mKeyboardLayouts.get(index); + return true; + } + + public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) { + boolean changed = false; + for (int i = mKeyboardLayouts.size(); i-- > 0; ) { + String keyboardLayout = mKeyboardLayouts.get(i); + if (!availableKeyboardLayouts.contains(keyboardLayout)) { + Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout); + mKeyboardLayouts.remove(i); + updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i); + changed = true; + } + } + return changed; + } + + public void loadFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("keyboard-layout")) { + String descriptor = parser.getAttributeValue(null, "descriptor"); + if (descriptor == null) { + throw new XmlPullParserException( + "Missing descriptor attribute on keyboard-layout."); + } + String current = parser.getAttributeValue(null, "current"); + if (mKeyboardLayouts.contains(descriptor)) { + throw new XmlPullParserException( + "Found duplicate keyboard layout."); + } + + mKeyboardLayouts.add(descriptor); + if (current != null && current.equals("true")) { + if (mCurrentKeyboardLayout != null) { + throw new XmlPullParserException( + "Found multiple current keyboard layouts."); + } + mCurrentKeyboardLayout = descriptor; + } + } + } + + // Maintain invariant that layouts are sorted. + Collections.sort(mKeyboardLayouts); + + // Maintain invariant that there is always a current keyboard layout unless + // there are none installed. + if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) { + mCurrentKeyboardLayout = mKeyboardLayouts.get(0); + } + } + + public void saveToXml(XmlSerializer serializer) throws IOException { + for (String layout : mKeyboardLayouts) { + serializer.startTag(null, "keyboard-layout"); + serializer.attribute(null, "descriptor", layout); + if (layout.equals(mCurrentKeyboardLayout)) { + serializer.attribute(null, "current", "true"); + } + serializer.endTag(null, "keyboard-layout"); + } + } + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 076ba9a..885ec96 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5007,6 +5007,12 @@ public class WindowManagerService extends IWindowManager.Stub // Called by window manager policy. Not exposed externally. @Override + public void switchKeyboardLayout(int deviceId, int direction) { + mInputManager.switchKeyboardLayout(deviceId, direction); + } + + // Called by window manager policy. Not exposed externally. + @Override public void shutdown() { ShutdownThread.shutdown(mContext, true); } |