import android.view.InputDevice;
import android.view.InputEvent;
import android.view.PointerIcon;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
/** @hide */
interface IInputManager {
String keyboardLayoutDescriptor);
void removeKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor);
+ KeyboardLayout getKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
+ in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype);
+ void setKeyboardLayoutForInputDevice(in InputDeviceIdentifier identifier,
+ in InputMethodInfo imeInfo, in InputMethodSubtype imeSubtype,
+ String keyboardLayoutDescriptor);
// Registers an input devices changed listener.
void registerInputDevicesChangedListener(IInputDevicesChangedListener listener);
package android.hardware.input;
-import android.view.PointerIcon;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;
import android.util.SparseArray;
import android.view.InputDevice;
import android.view.InputEvent;
+import android.view.PointerIcon;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
}
}
+
+ /**
+ * Gets the keyboard layout for the specified input device and IME subtype.
+ *
+ * @param identifier The identifier for the input device.
+ * @param inputMethodInfo The input method.
+ * @param inputMethodSubtype The input method subtype.
+ *
+ * @return The associated {@link KeyboardLayout}, or null if one has not been set.
+ *
+ * @hide
+ */
+ public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+ InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype) {
+ try {
+ return mIm.getKeyboardLayoutForInputDevice(
+ identifier, inputMethodInfo, inputMethodSubtype);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not set keyboard layout.", ex);
+ return null;
+ }
+ }
+
+ /**
+ * Sets the keyboard layout for the specified input device and IME subtype pair.
+ *
+ * @param identifier The identifier for the input device.
+ * @param inputMethodInfo The input method with which to associate the keyboard layout.
+ * @param inputMethodSubtype The input method subtype which which to associate the keyboard
+ * layout.
+ * @param keyboardLayoutDescriptor The descriptor of the keyboard layout to set
+ *
+ * @hide
+ */
+ public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+ InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype,
+ String keyboardLayoutDescriptor) {
+ try {
+ mIm.setKeyboardLayoutForInputDevice(identifier, inputMethodInfo,
+ inputMethodSubtype, keyboardLayoutDescriptor);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not set keyboard layout.", ex);
+ }
+ }
+
/**
* Gets the TouchCalibration applied to the specified input device's coordinates.
*
Float.floatToIntBits(mYScale) ^
Float.floatToIntBits(mYOffset);
}
+
+ @Override
+ public String toString() {
+ return String.format("[%f, %f, %f, %f, %f, %f]",
+ mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset);
+ }
}
public int getCameraLensCoverState();
/**
- * 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);
-
- /**
* Switch the input method, to be precise, input method subtype.
*
* @param forwardDirection {@code true} to rotate in a forward direction.
--- /dev/null
+/*
+ * Copyright (C) 2016 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.internal.inputmethod;
+
+import android.text.TextUtils;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import java.util.Objects;
+
+public class InputMethodSubtypeHandle {
+ private final String mInputMethodId;
+ private final int mSubtypeId;
+
+ public InputMethodSubtypeHandle(InputMethodInfo info, InputMethodSubtype subtype) {
+ mInputMethodId = info.getId();
+ if (subtype != null) {
+ mSubtypeId = subtype.hashCode();
+ } else {
+ mSubtypeId = 0;
+ }
+ }
+
+ public InputMethodSubtypeHandle(String inputMethodId, int subtypeId) {
+ mInputMethodId = inputMethodId;
+ mSubtypeId = subtypeId;
+ }
+
+ public String getInputMethodId() {
+ return mInputMethodId;
+ }
+
+ public int getSubtypeId() {
+ return mSubtypeId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof InputMethodSubtypeHandle)) {
+ return false;
+ }
+ InputMethodSubtypeHandle other = (InputMethodSubtypeHandle) o;
+ return TextUtils.equals(mInputMethodId, other.getInputMethodId())
+ && mSubtypeId == other.getSubtypeId();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mInputMethodId) * 31 + mSubtypeId;
+ }
+
+ @Override
+ public String toString() {
+ return "InputMethodSubtypeHandle{mInputMethodId=" + mInputMethodId
+ + ", mSubtypeId=" + mSubtypeId + "}";
+ }
+}
import android.annotation.Nullable;
import android.view.Display;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
import com.android.internal.R;
import com.android.internal.util.XmlUtils;
import android.os.MessageQueue;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCommand;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
private final ArrayList<InputDevice>
mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
private boolean mKeyboardLayoutNotificationShown;
- private PendingIntent mKeyboardLayoutIntent;
- private Toast mSwitchedKeyboardLayoutToast;
+ private InputMethodSubtypeHandle mCurrentImeHandle;
// State for vibrator tokens.
private Object mVibratorLock = new Object();
}
@Override // Binder call
+ @Nullable
+ public KeyboardLayout getKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+ InputMethodInfo imeInfo, InputMethodSubtype imeSubtype) {
+ InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(imeInfo, imeSubtype);
+ String key = getLayoutDescriptor(identifier);
+ final String keyboardLayoutDescriptor;
+ synchronized (mDataStore) {
+ keyboardLayoutDescriptor = mDataStore.getKeyboardLayout(key, handle);
+ }
+
+ if (keyboardLayoutDescriptor == null) {
+ return null;
+ }
+
+ final KeyboardLayout[] result = new KeyboardLayout[1];
+ visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
+ @Override
+ public void visitKeyboardLayout(Resources resources,
+ int keyboardLayoutResId, KeyboardLayout layout) {
+ result[0] = layout;
+ }
+ });
+ if (result[0] == null) {
+ Slog.w(TAG, "Could not get keyboard layout with descriptor '"
+ + keyboardLayoutDescriptor + "'.");
+ }
+ return result[0];
+ }
+
+ @Override
+ public void setKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
+ InputMethodInfo imeInfo, InputMethodSubtype imeSubtype,
+ String keyboardLayoutDescriptor) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+ "setKeyboardLayoutForInputDevice()")) {
+ throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+ }
+ if (keyboardLayoutDescriptor == null) {
+ throw new IllegalArgumentException("keyboardLayoutDescriptor must not be null");
+ }
+ if (imeInfo == null || imeSubtype == null) {
+ throw new IllegalArgumentException("imeInfo and imeSubtype must not be null");
+ }
+ InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(imeInfo, imeSubtype);
+ setKeyboardLayoutForInputDeviceInner(identifier, handle, keyboardLayoutDescriptor);
+ }
+
+ private void setKeyboardLayoutForInputDeviceInner(InputDeviceIdentifier identifier,
+ InputMethodSubtypeHandle imeHandle, String keyboardLayoutDescriptor) {
+ String key = getLayoutDescriptor(identifier);
+ synchronized (mDataStore) {
+ try {
+ if (mDataStore.setKeyboardLayout(key, imeHandle, keyboardLayoutDescriptor)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Set keyboard layout " + keyboardLayoutDescriptor +
+ " for subtype " + imeHandle + " and device " + identifier +
+ " using key " + key);
+ }
+ if (imeHandle.equals(mCurrentImeHandle)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Layout for current subtype changed, switching layout");
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = identifier;
+ args.arg2 = imeHandle;
+ mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, args).sendToTarget();
+ }
+ mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
+ }
+ } finally {
+ mDataStore.saveIfNeeded();
+ }
+ }
+ }
+
+ @Override // Binder call
public void addKeyboardLayoutForInputDevice(InputDeviceIdentifier identifier,
String keyboardLayoutDescriptor) {
if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
oldLayout = mDataStore.getCurrentKeyboardLayout(identifier.getDescriptor());
}
if (mDataStore.addKeyboardLayout(key, keyboardLayoutDescriptor)
- && !Objects.equal(oldLayout,
- mDataStore.getCurrentKeyboardLayout(key))) {
+ && !Objects.equal(oldLayout, mDataStore.getCurrentKeyboardLayout(key))) {
mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS);
}
} finally {
Slog.i(TAG, "InputMethodSubtype changed: userId=" + userId
+ " ime=" + inputMethodInfo + " subtype=" + subtype);
}
- }
-
- public void switchKeyboardLayout(int deviceId, int direction) {
- mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, deviceId, direction).sendToTarget();
+ if (inputMethodInfo == null) {
+ Slog.d(TAG, "No InputMethod is running, ignoring change");
+ return;
+ }
+ if (subtype != null && !"keyboard".equals(subtype.getMode())) {
+ Slog.d(TAG, "InputMethodSubtype changed to non-keyboard subtype, ignoring change");
+ return;
+ }
+ InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(inputMethodInfo, subtype);
+ if (!handle.equals(mCurrentImeHandle)) {
+ mCurrentImeHandle = handle;
+ handleSwitchKeyboardLayout(null, handle);
+ }
}
// Must be called on handler.
- private void handleSwitchKeyboardLayout(int deviceId, int direction) {
- final InputDevice device = getInputDevice(deviceId);
- if (device != null) {
- final boolean changed;
- final String keyboardLayoutDescriptor;
-
- String key = getLayoutDescriptor(device.getIdentifier());
- synchronized (mDataStore) {
- try {
- changed = mDataStore.switchKeyboardLayout(key, direction);
- keyboardLayoutDescriptor = mDataStore.getCurrentKeyboardLayout(
- key);
- } finally {
- mDataStore.saveIfNeeded();
- }
- }
-
- if (changed) {
- if (mSwitchedKeyboardLayoutToast != null) {
- mSwitchedKeyboardLayoutToast.cancel();
- mSwitchedKeyboardLayoutToast = null;
+ private void handleSwitchKeyboardLayout(@Nullable InputDeviceIdentifier identifier,
+ InputMethodSubtypeHandle handle) {
+ synchronized (mInputDevicesLock) {
+ for (InputDevice device : mInputDevices) {
+ if (identifier != null && !device.getIdentifier().equals(identifier) ||
+ !device.isFullKeyboard()) {
+ continue;
}
- if (keyboardLayoutDescriptor != null) {
- KeyboardLayout keyboardLayout = getKeyboardLayout(keyboardLayoutDescriptor);
- if (keyboardLayout != null) {
- mSwitchedKeyboardLayoutToast = Toast.makeText(
- mContext, keyboardLayout.getLabel(), Toast.LENGTH_SHORT);
- mSwitchedKeyboardLayoutToast.show();
+ String key = getLayoutDescriptor(device.getIdentifier());
+ boolean changed = false;
+ synchronized (mDataStore) {
+ try {
+ if (mDataStore.switchKeyboardLayout(key, handle)) {
+ changed = true;
+ }
+ } finally {
+ mDataStore.saveIfNeeded();
}
}
-
- reloadKeyboardLayouts();
+ if (changed) {
+ reloadKeyboardLayouts();
+ }
}
}
}
}
@Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump InputManager from from pid="
if (dumpStr != null) {
pw.println(dumpStr);
}
+ pw.println(" Keyboard Layouts:");
+ visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
+ @Override
+ public void visitKeyboardLayout(Resources resources,
+ int keyboardLayoutResId, KeyboardLayout layout) {
+ pw.println(" \"" + layout + "\": " + layout.getDescriptor());
+ }
+ });
+ pw.println();
+ synchronized(mDataStore) {
+ mDataStore.dump(pw, " ");
+ }
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+ (new Shell()).exec(this, in, out, err, args, resultReceiver);
+ }
+
+ public int onShellCommand(Shell shell, String cmd) {
+ if (TextUtils.isEmpty(cmd)) {
+ shell.onHelp();
+ return 1;
+ }
+ if (cmd.equals("setlayout")) {
+ if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT,
+ "onShellCommand()")) {
+ throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission");
+ }
+ InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(
+ shell.getNextArgRequired(), Integer.parseInt(shell.getNextArgRequired()));
+ String descriptor = shell.getNextArgRequired();
+ int vid = Integer.decode(shell.getNextArgRequired());
+ int pid = Integer.decode(shell.getNextArgRequired());
+ InputDeviceIdentifier id = new InputDeviceIdentifier(descriptor, vid, pid);
+ setKeyboardLayoutForInputDeviceInner(id, handle, shell.getNextArgRequired());
+ }
+ return 0;
}
+
private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
case MSG_DELIVER_INPUT_DEVICES_CHANGED:
deliverInputDevicesChanged((InputDevice[])msg.obj);
break;
- case MSG_SWITCH_KEYBOARD_LAYOUT:
- handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+ case MSG_SWITCH_KEYBOARD_LAYOUT: {
+ SomeArgs args = (SomeArgs)msg.obj;
+ handleSwitchKeyboardLayout((InputDeviceIdentifier)args.arg1,
+ (InputMethodSubtypeHandle)args.arg2);
break;
+ }
case MSG_RELOAD_KEYBOARD_LAYOUTS:
reloadKeyboardLayouts();
break;
}
}
+ private class Shell extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ return onShellCommand(this, cmd);
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Input manager commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" setlayout IME_ID IME_SUPTYPE_HASH_CODE"
+ + " DEVICE_DESCRIPTOR VENDOR_ID PRODUCT_ID KEYBOARD_DESCRIPTOR");
+ pw.println(" Sets a keyboard layout for a given IME subtype and input device pair");
+ }
+ }
+
private final class LocalService extends InputManagerInternal {
@Override
public void setDisplayViewports(
package com.android.server.input;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import android.view.Surface;
import android.hardware.input.TouchCalibration;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
}
return state.getKeyboardLayouts();
}
+ public String getKeyboardLayout(String inputDeviceDescriptor,
+ InputMethodSubtypeHandle imeHandle) {
+ InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+ if (state == null) {
+ return null;
+ }
+ return state.getKeyboardLayout(imeHandle);
+ }
- public boolean addKeyboardLayout(String inputDeviceDescriptor,
- String keyboardLayoutDescriptor) {
+ public boolean setKeyboardLayout(String inputDeviceDescriptor,
+ InputMethodSubtypeHandle imeHandle, String keyboardLayoutDescriptor) {
+ InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+ if (state.setKeyboardLayout(imeHandle, keyboardLayoutDescriptor)) {
+ setDirty();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean addKeyboardLayout(String inputDeviceDescriptor, String keyboardLayoutDescriptor) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
if (state.addKeyboardLayout(keyboardLayoutDescriptor)) {
setDirty();
return false;
}
- public boolean switchKeyboardLayout(String inputDeviceDescriptor, int direction) {
+ public boolean switchKeyboardLayout(String inputDeviceDescriptor,
+ InputMethodSubtypeHandle imeHandle) {
InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
- if (state != null && state.switchKeyboardLayout(direction)) {
+ if (state != null && state.switchKeyboardLayout(imeHandle)) {
setDirty();
return true;
}
serializer.endDocument();
}
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "PersistentDataStore");
+ pw.println(prefix + " mLoaded=" + mLoaded);
+ pw.println(prefix + " mDirty=" + mDirty);
+ pw.println(prefix + " InputDeviceStates:");
+ int i = 0;
+ for (Map.Entry<String, InputDeviceState> entry : mInputDevices.entrySet()) {
+ pw.println(prefix + " " + i++ + ": " + entry.getKey());
+ entry.getValue().dump(pw, prefix + " ");
+ }
+ }
+
private static final class InputDeviceState {
private static final String[] CALIBRATION_NAME = { "x_scale",
"x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
private String mCurrentKeyboardLayout;
- private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
+ private List<String> mUnassociatedKeyboardLayouts = new ArrayList<>();
+ private ArrayMap<InputMethodSubtypeHandle, String> mKeyboardLayouts = new ArrayMap<>();
public TouchCalibration getTouchCalibration(int surfaceRotation) {
try {
}
public String[] getKeyboardLayouts() {
- if (mKeyboardLayouts.isEmpty()) {
+ if (mUnassociatedKeyboardLayouts.isEmpty()) {
return (String[])ArrayUtils.emptyArray(String.class);
}
- return mKeyboardLayouts.toArray(new String[mKeyboardLayouts.size()]);
+ return mUnassociatedKeyboardLayouts.toArray(
+ new String[mUnassociatedKeyboardLayouts.size()]);
+ }
+
+ public String getKeyboardLayout(InputMethodSubtypeHandle handle) {
+ return mKeyboardLayouts.get(handle);
+ }
+
+ public boolean setKeyboardLayout(InputMethodSubtypeHandle imeHandle,
+ String keyboardLayout) {
+ String existingLayout = mKeyboardLayouts.get(imeHandle);
+ if (TextUtils.equals(existingLayout, keyboardLayout)) {
+ return false;
+ }
+ mKeyboardLayouts.put(imeHandle, keyboardLayout);
+ return true;
}
public boolean addKeyboardLayout(String keyboardLayout) {
- int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+ int index = Collections.binarySearch(
+ mUnassociatedKeyboardLayouts, keyboardLayout);
if (index >= 0) {
return false;
}
- mKeyboardLayouts.add(-index - 1, keyboardLayout);
+ mUnassociatedKeyboardLayouts.add(-index - 1, keyboardLayout);
if (mCurrentKeyboardLayout == null) {
mCurrentKeyboardLayout = keyboardLayout;
}
}
public boolean removeKeyboardLayout(String keyboardLayout) {
- int index = Collections.binarySearch(mKeyboardLayouts, keyboardLayout);
+ int index = Collections.binarySearch(mUnassociatedKeyboardLayouts, keyboardLayout);
if (index < 0) {
return false;
}
- mKeyboardLayouts.remove(index);
+ mUnassociatedKeyboardLayouts.remove(index);
updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, index);
return true;
}
private void updateCurrentKeyboardLayoutIfRemoved(
String removedKeyboardLayout, int removedIndex) {
if (Objects.equal(mCurrentKeyboardLayout, removedKeyboardLayout)) {
- if (!mKeyboardLayouts.isEmpty()) {
+ if (!mUnassociatedKeyboardLayouts.isEmpty()) {
int index = removedIndex;
- if (index == mKeyboardLayouts.size()) {
+ if (index == mUnassociatedKeyboardLayouts.size()) {
index = 0;
}
- mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
+ mCurrentKeyboardLayout = mUnassociatedKeyboardLayouts.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;
+ public boolean switchKeyboardLayout(InputMethodSubtypeHandle imeHandle) {
+ final String layout = mKeyboardLayouts.get(imeHandle);
+ if (layout != null && !TextUtils.equals(mCurrentKeyboardLayout, layout)) {
+ mCurrentKeyboardLayout = layout;
+ return true;
}
- mCurrentKeyboardLayout = mKeyboardLayouts.get(index);
- return true;
+ return false;
}
public boolean removeUninstalledKeyboardLayouts(Set<String> availableKeyboardLayouts) {
boolean changed = false;
- for (int i = mKeyboardLayouts.size(); i-- > 0; ) {
- String keyboardLayout = mKeyboardLayouts.get(i);
+ for (int i = mUnassociatedKeyboardLayouts.size(); i-- > 0; ) {
+ String keyboardLayout = mUnassociatedKeyboardLayouts.get(i);
if (!availableKeyboardLayouts.contains(keyboardLayout)) {
Slog.i(TAG, "Removing uninstalled keyboard layout " + keyboardLayout);
- mKeyboardLayouts.remove(i);
+ mUnassociatedKeyboardLayouts.remove(i);
updateCurrentKeyboardLayoutIfRemoved(keyboardLayout, i);
changed = true;
}
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);
+ String current = parser.getAttributeValue(null, "current");
if (current != null && current.equals("true")) {
if (mCurrentKeyboardLayout != null) {
throw new XmlPullParserException(
}
mCurrentKeyboardLayout = descriptor;
}
+
+ String inputMethodId = parser.getAttributeValue(null, "input-method-id");
+ String inputMethodSubtypeId =
+ parser.getAttributeValue(null, "input-method-subtype-id");
+ if (inputMethodId == null && inputMethodSubtypeId != null
+ || inputMethodId != null && inputMethodSubtypeId == null) {
+ throw new XmlPullParserException(
+ "Found an incomplete input method description");
+ }
+
+ if (inputMethodSubtypeId != null) {
+ InputMethodSubtypeHandle handle = new InputMethodSubtypeHandle(
+ inputMethodId, Integer.parseInt(inputMethodSubtypeId));
+ if (mKeyboardLayouts.containsKey(handle)) {
+ throw new XmlPullParserException(
+ "Found duplicate subtype to keyboard layout mapping: "
+ + handle);
+ }
+ mKeyboardLayouts.put(handle, descriptor);
+ } else {
+ if (mUnassociatedKeyboardLayouts.contains(descriptor)) {
+ throw new XmlPullParserException(
+ "Found duplicate unassociated keyboard layout: " + descriptor);
+ }
+ mUnassociatedKeyboardLayouts.add(descriptor);
+ }
} else if (parser.getName().equals("calibration")) {
String format = parser.getAttributeValue(null, "format");
String rotation = parser.getAttributeValue(null, "rotation");
}
// Maintain invariant that layouts are sorted.
- Collections.sort(mKeyboardLayouts);
+ Collections.sort(mUnassociatedKeyboardLayouts);
// Maintain invariant that there is always a current keyboard layout unless
// there are none installed.
- if (mCurrentKeyboardLayout == null && !mKeyboardLayouts.isEmpty()) {
- mCurrentKeyboardLayout = mKeyboardLayouts.get(0);
+ if (mCurrentKeyboardLayout == null && !mUnassociatedKeyboardLayouts.isEmpty()) {
+ mCurrentKeyboardLayout = mUnassociatedKeyboardLayouts.get(0);
}
}
public void saveToXml(XmlSerializer serializer) throws IOException {
- for (String layout : mKeyboardLayouts) {
+ for (String layout : mUnassociatedKeyboardLayouts) {
+ serializer.startTag(null, "keyboard-layout");
+ serializer.attribute(null, "descriptor", layout);
+ serializer.endTag(null, "keyboard-layout");
+ }
+
+ final int N = mKeyboardLayouts.size();
+ for (int i = 0; i < N; i++) {
+ final InputMethodSubtypeHandle handle = mKeyboardLayouts.keyAt(i);
+ final String layout = mKeyboardLayouts.valueAt(i);
serializer.startTag(null, "keyboard-layout");
serializer.attribute(null, "descriptor", layout);
+ serializer.attribute(null, "input-method-id", handle.getInputMethodId());
+ serializer.attribute(null, "input-method-subtype-id",
+ Integer.toString(handle.getSubtypeId()));
if (layout.equals(mCurrentKeyboardLayout)) {
serializer.attribute(null, "current", "true");
}
}
}
+ private void dump(final PrintWriter pw, final String prefix) {
+ pw.println(prefix + "CurrentKeyboardLayout=" + mCurrentKeyboardLayout);
+ pw.println(prefix + "UnassociatedKeyboardLayouts=" + mUnassociatedKeyboardLayouts);
+ pw.println(prefix + "TouchCalibration=" + Arrays.toString(mTouchCalibration));
+ pw.println(prefix + "Subtype to Layout Mappings:");
+ final int N = mKeyboardLayouts.size();
+ if (N != 0) {
+ for (int i = 0; i < N; i++) {
+ pw.println(prefix + " " + mKeyboardLayouts.keyAt(i) + ": "
+ + mKeyboardLayouts.valueAt(i));
+ }
+ } else {
+ pw.println(prefix + " <none>");
+ }
+ }
+
private static String surfaceRotationToString(int surfaceRotation) {
switch (surfaceRotation) {
case Surface.ROTATION_0: return "0";
hideRecentApps(true, false);
}
- // Handle keyboard layout switching.
- // TODO: Deprecate this behavior when we fully migrate to IME subtype-based layout rotation.
- if (down && repeatCount == 0 && 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;
- }
-
// Handle input method switching.
if (down && repeatCount == 0
&& (keyCode == KeyEvent.KEYCODE_LANGUAGE_SWITCH
// 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 switchInputMethod(boolean forwardDirection) {
final InputMethodManagerInternal inputMethodManagerInternal =
LocalServices.getService(InputMethodManagerInternal.class);