Merge "Only reset time from NITZ in zone fix." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index 2c02347..81efd34 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -9243,7 +9243,6 @@
method public int getMinimumWidth();
method public abstract int getOpacity();
method public boolean getPadding(android.graphics.Rect);
- method public int getResolvedLayoutDirectionSelf();
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -27329,7 +27328,7 @@
field public int gravity;
}
- public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+ public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
ctor public Gallery(android.content.Context);
ctor public Gallery(android.content.Context, android.util.AttributeSet);
ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28620,8 +28619,6 @@
method public void onEndBatchEdit();
method public boolean onPreDraw();
method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
- method public void onResolvedLayoutDirectionReset();
- method public void onResolvedTextDirectionChanged();
method public void onRestoreInstanceState(android.os.Parcelable);
method public android.os.Parcelable onSaveInstanceState();
method protected void onSelectionChanged(int, int);
diff --git a/api/current.txt b/api/current.txt
index 2c02347..81efd34 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9243,7 +9243,6 @@
method public int getMinimumWidth();
method public abstract int getOpacity();
method public boolean getPadding(android.graphics.Rect);
- method public int getResolvedLayoutDirectionSelf();
method public int[] getState();
method public android.graphics.Region getTransparentRegion();
method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -27329,7 +27328,7 @@
field public int gravity;
}
- public class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
+ public deprecated class Gallery extends android.widget.AbsSpinner implements android.view.GestureDetector.OnGestureListener {
ctor public Gallery(android.content.Context);
ctor public Gallery(android.content.Context, android.util.AttributeSet);
ctor public Gallery(android.content.Context, android.util.AttributeSet, int);
@@ -28620,8 +28619,6 @@
method public void onEndBatchEdit();
method public boolean onPreDraw();
method public boolean onPrivateIMECommand(java.lang.String, android.os.Bundle);
- method public void onResolvedLayoutDirectionReset();
- method public void onResolvedTextDirectionChanged();
method public void onRestoreInstanceState(android.os.Parcelable);
method public android.os.Parcelable onSaveInstanceState();
method protected void onSelectionChanged(int, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index faf946c..f20fd33 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3273,7 +3273,7 @@
if (mMenuInflater == null) {
initActionBar();
if (mActionBar != null) {
- mMenuInflater = new MenuInflater(mActionBar.getThemedContext());
+ mMenuInflater = new MenuInflater(mActionBar.getThemedContext(), this);
} else {
mMenuInflater = new MenuInflater(this);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2eea171..c7afd2b2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1850,7 +1850,7 @@
contentView.setViewVisibility(R.id.text2, View.GONE);
int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
- R.id.inbox_text4};
+ R.id.inbox_text4, R.id.inbox_text5, R.id.inbox_text6};
// Make sure all rows are gone in case we reuse a view.
for (int rowId : rowIds) {
@@ -1867,6 +1867,12 @@
i++;
}
+ if (mTexts.size() > rowIds.length) {
+ contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.inbox_more, View.GONE);
+ }
+
return contentView;
}
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 @@
// 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 @@
}
/**
+ * 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 @@
}
/**
- * 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, or null to remove
- * the mapping so that the default keyboard layout will be used for the input device.
+ * @param keyboardLayoutDescriptor The keyboard layout descriptor to use, must not be null.
*
* @hide
*/
- public void setKeyboardLayoutForInputDevice(String inputDeviceDescriptor,
+ 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 {
- mIm.setKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+ return mIm.getKeyboardLayoutsForInputDevice(inputDeviceDescriptor);
} catch (RemoteException ex) {
- Log.w(TAG, "Could not set keyboard layout for input device.", 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 descriptor of the keyboard layout to add.
+ *
+ * @hide
+ */
+ 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.removeKeyboardLayoutForInputDevice(inputDeviceDescriptor, keyboardLayoutDescriptor);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Could not remove keyboard layout for input device.", ex);
}
}
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index c46e43a..a7ee12b 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -65,6 +65,7 @@
private final Object[] mActionProviderConstructorArguments;
private Context mContext;
+ private Object mRealOwner;
/**
* Constructs a menu inflater.
@@ -73,6 +74,20 @@
*/
public MenuInflater(Context context) {
mContext = context;
+ mRealOwner = context;
+ mActionViewConstructorArguments = new Object[] {context};
+ mActionProviderConstructorArguments = mActionViewConstructorArguments;
+ }
+
+ /**
+ * Constructs a menu inflater.
+ *
+ * @see Activity#getMenuInflater()
+ * @hide
+ */
+ public MenuInflater(Context context, Object realOwner) {
+ mContext = context;
+ mRealOwner = realOwner;
mActionViewConstructorArguments = new Object[] {context};
mActionProviderConstructorArguments = mActionViewConstructorArguments;
}
@@ -190,12 +205,12 @@
implements MenuItem.OnMenuItemClickListener {
private static final Class<?>[] PARAM_TYPES = new Class[] { MenuItem.class };
- private Context mContext;
+ private Object mRealOwner;
private Method mMethod;
- public InflatedOnMenuItemClickListener(Context context, String methodName) {
- mContext = context;
- Class<?> c = context.getClass();
+ public InflatedOnMenuItemClickListener(Object realOwner, String methodName) {
+ mRealOwner = realOwner;
+ Class<?> c = realOwner.getClass();
try {
mMethod = c.getMethod(methodName, PARAM_TYPES);
} catch (Exception e) {
@@ -210,9 +225,9 @@
public boolean onMenuItemClick(MenuItem item) {
try {
if (mMethod.getReturnType() == Boolean.TYPE) {
- return (Boolean) mMethod.invoke(mContext, item);
+ return (Boolean) mMethod.invoke(mRealOwner, item);
} else {
- mMethod.invoke(mContext, item);
+ mMethod.invoke(mRealOwner, item);
return true;
}
} catch (Exception e) {
@@ -400,7 +415,7 @@
+ "be used within a restricted context");
}
item.setOnMenuItemClickListener(
- new InflatedOnMenuItemClickListener(mContext, itemListenerMethodName));
+ new InflatedOnMenuItemClickListener(mRealOwner, itemListenerMethodName));
}
if (item instanceof MenuItemImpl) {
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 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/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 6658fc2..e4e7239 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -56,7 +56,12 @@
* @attr ref android.R.styleable#Gallery_animationDuration
* @attr ref android.R.styleable#Gallery_spacing
* @attr ref android.R.styleable#Gallery_gravity
+ *
+ * @deprecated This widget is no longer supported. Other horizontally scrolling
+ * widgets include {@link HorizontalScrollView} and {@link android.support.v4.view.ViewPager}
+ * from the support library.
*/
+@Deprecated
@Widget
public class Gallery extends AbsSpinner implements GestureDetector.OnGestureListener {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 112881c..aef7929 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5625,6 +5625,7 @@
physicalWidth, false);
}
+ /** @hide */
@Override
public void onResolvedLayoutDirectionReset() {
if (mLayoutAlignment != null) {
@@ -8161,6 +8162,7 @@
return mEditor.mInBatchEditControllers;
}
+ /** @hide */
@Override
public void onResolvedTextDirectionChanged() {
if (hasPasswordTransformationMethod()) {
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 88d7e05..fafc113 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -120,7 +120,12 @@
*/
public void cancel() {
mTN.hide();
- // TODO this still needs to cancel the inflight notification if any
+
+ try {
+ getService().cancelToast(mContext.getPackageName(), mTN);
+ } catch (RemoteException e) {
+ // Empty
+ }
}
/**
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index e3d11f2..82f8984 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -774,11 +774,12 @@
float transform[16];
sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
- surfaceTexture->updateTexImage();
- surfaceTexture->getTransformMatrix(transform);
- GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
+ if (surfaceTexture->updateTexImage() == NO_ERROR) {
+ surfaceTexture->getTransformMatrix(transform);
+ GLenum renderTarget = surfaceTexture->getCurrentTextureTarget();
- LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+ LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
+ }
}
static void android_view_GLES20Canvas_updateRenderLayer(JNIEnv* env, jobject clazz,
diff --git a/core/res/res/drawable-hdpi/ic_settings_language.png b/core/res/res/drawable-hdpi/ic_settings_language.png
new file mode 100755
index 0000000..f635b2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_settings_language.png b/core/res/res/drawable-mdpi/ic_settings_language.png
new file mode 100644
index 0000000..f8aca67
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_settings_language.png b/core/res/res/drawable-xhdpi/ic_settings_language.png
new file mode 100644
index 0000000..2c42db3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_settings_language.png
Binary files differ
diff --git a/core/res/res/layout/notification_template_inbox.xml b/core/res/res/layout/notification_template_inbox.xml
index eb5e759..3b00feb 100644
--- a/core/res/res/layout/notification_template_inbox.xml
+++ b/core/res/res/layout/notification_template_inbox.xml
@@ -132,6 +132,34 @@
android:visibility="gone"
android:layout_weight="1"
/>
+ <TextView android:id="@+id/inbox_text5"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_more"
+ android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ android:text="@android:string/ellipsis"
+ />
<include
layout="@layout/notification_action_list"
android:id="@+id/actions"
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a24e345c..9829d89 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -208,6 +208,9 @@
<java-symbol type="id" name="inbox_text2" />
<java-symbol type="id" name="inbox_text3" />
<java-symbol type="id" name="inbox_text4" />
+ <java-symbol type="id" name="inbox_text5" />
+ <java-symbol type="id" name="inbox_text6" />
+ <java-symbol type="id" name="inbox_more" />
<java-symbol type="id" name="status_bar_latest_event_content" />
<java-symbol type="attr" name="actionModeShareDrawable" />
@@ -1489,6 +1492,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 +1585,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 4511201..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>
@@ -3572,6 +3577,6 @@
<!-- Title for a button to choose the currently selected activity
from the activity resolver to use just this once. [CHAR LIMIT=25] -->
- <string name="activity_resolver_use_once">Just Once</string>
+ <string name="activity_resolver_use_once">Just once</string>
</resources>
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 @@
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 @@
label: ' '
base: ' '
alt, meta: fallback SEARCH
+ ctrl: fallback LANGUAGE_SWITCH
}
key ENTER {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 5f74c01..785582c 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -386,6 +386,7 @@
/**
* Get the resolved layout direction of this Drawable.
+ * @hide
*/
public int getResolvedLayoutDirectionSelf() {
final Callback callback = getCallback();
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
index 3657d8a..d8aa40f 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosink/MediaEncoderFilter.java
@@ -376,8 +376,6 @@
@Override
public void process(FilterContext context) {
- if (mLogVerbose) Log.v(TAG, "Starting frame processing");
-
GLEnvironment glEnv = context.getGLEnvironment();
// Get input frame
Frame input = pullInput("videoframe");
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
index b023e42..674a2bd 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -121,6 +121,7 @@
}
public void updateRenderMode() {
+ if (mLogVerbose) Log.v(TAG, "updateRenderMode. Thread: " + Thread.currentThread());
if (mRenderModeString != null) {
if (mRenderModeString.equals("stretch")) {
mRenderMode = RENDERMODE_STRETCH;
@@ -139,6 +140,7 @@
@Override
public void prepare(FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "Prepare. Thread: " + Thread.currentThread());
// Create identity shader to render, and make sure to render upside-down, as textures
// are stored internally bottom-to-top.
mProgram = ShaderProgram.createIdentity(context);
@@ -214,8 +216,10 @@
float currentAspectRatio =
(float)input.getFormat().getWidth() / input.getFormat().getHeight();
if (currentAspectRatio != mAspectRatio) {
- if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +
- ", previously: " + mAspectRatio);
+ if (mLogVerbose) {
+ Log.v(TAG, "Process. New aspect ratio: " + currentAspectRatio +
+ ", previously: " + mAspectRatio + ". Thread: " + Thread.currentThread());
+ }
mAspectRatio = currentAspectRatio;
updateTargetRect();
}
@@ -249,6 +253,7 @@
@Override
public void fieldPortValueUpdated(String name, FilterContext context) {
+ if (mLogVerbose) Log.v(TAG, "FPVU. Thread: " + Thread.currentThread());
updateRenderMode();
}
@@ -260,16 +265,22 @@
}
private void updateTargetRect() {
+ if (mLogVerbose) Log.v(TAG, "updateTargetRect. Thread: " + Thread.currentThread());
if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
float relativeAspectRatio = screenAspectRatio / mAspectRatio;
+ if (mLogVerbose) {
+ Log.v(TAG, "UTR. screen w = " + (float)mScreenWidth + " x screen h = " +
+ (float)mScreenHeight + " Screen AR: " + screenAspectRatio +
+ ", frame AR: " + mAspectRatio + ", relative AR: " + relativeAspectRatio);
+ }
if (relativeAspectRatio == 1.0f && mRenderMode != RENDERMODE_CUSTOMIZE) {
+ mProgram.setTargetRect(0, 0, 1, 1);
mProgram.setClearsOutput(false);
} else {
switch (mRenderMode) {
case RENDERMODE_STRETCH:
- mProgram.setTargetRect(0, 0, 1, 1);
mTargetQuad.p0.set(0f, 0.0f);
mTargetQuad.p1.set(1f, 0.0f);
mTargetQuad.p2.set(0f, 1.0f);
@@ -313,6 +324,7 @@
((ShaderProgram) mProgram).setSourceRegion(mSourceQuad);
break;
}
+ if (mLogVerbose) Log.v(TAG, "UTR. quad: " + mTargetQuad);
((ShaderProgram) mProgram).setTargetRegion(mTargetQuad);
}
}
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 @@
RecentApplicationsDialog mRecentAppsDialog;
int mRecentAppsDialogHeldModifiers;
+ boolean mLanguageSwitchKeyPressed;
int mLidState = LID_ABSENT;
boolean mHaveBuiltInKeyboard;
@@ -1943,6 +1944,22 @@
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/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index a49ccf7..ff14568 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -195,6 +195,7 @@
private PendingIntent mImeSwitchPendingIntent;
private boolean mShowOngoingImeSwitcherForPhones;
private boolean mNotificationShown;
+ private final boolean mImeSelectedOnBoot;
class SessionState {
final ClientState client;
@@ -590,7 +591,6 @@
mImeSwitcherNotification.vibrate = null;
Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- mLastSystemLocale = mRes.getConfiguration().locale;
mShowOngoingImeSwitcherForPhones = false;
@@ -612,11 +612,17 @@
// mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList);
+
+ // Just checking if defaultImiId is empty or not
+ final String defaultImiId = Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
+ mImeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
+
buildInputMethodListLocked(mMethodList, mMethodMap);
mSettings.enableAllIMEsIfThereIsNoEnabledIME();
- if (TextUtils.isEmpty(Settings.Secure.getString(
- mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD))) {
+ if (!mImeSelectedOnBoot) {
+ Slog.w(TAG, "No IME selected. Choose the most applicable IME.");
resetDefaultImeLocked(context);
}
@@ -639,6 +645,10 @@
}
private void checkCurrentLocaleChangedLocked() {
+ if (!mSystemReady) {
+ // not system ready
+ return;
+ }
final Locale newLocale = mRes.getConfiguration().locale;
if (newLocale != null && !newLocale.equals(mLastSystemLocale)) {
if (DEBUG) {
@@ -647,6 +657,7 @@
buildInputMethodListLocked(mMethodList, mMethodMap);
// Reset the current ime to the proper one
resetDefaultImeLocked(mContext);
+ updateFromSettingsLocked();
mLastSystemLocale = newLocale;
}
}
@@ -675,7 +686,10 @@
}
}
- private static boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+ private boolean isValidSystemDefaultIme(InputMethodInfo imi, Context context) {
+ if (!mSystemReady) {
+ return false;
+ }
if (!isSystemIme(imi)) {
return false;
}
@@ -738,7 +752,6 @@
mContext.getSystemService(Context.KEYGUARD_SERVICE);
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- mLastSystemLocale = mContext.getResources().getConfiguration().locale;
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
updateImeWindowStatusLocked();
@@ -748,6 +761,12 @@
mWindowManagerService.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
}
+ buildInputMethodListLocked(mMethodList, mMethodMap);
+ if (!mImeSelectedOnBoot) {
+ Slog.w(TAG, "Reset the default IME as \"Resource\" is ready here.");
+ checkCurrentLocaleChangedLocked();
+ }
+ mLastSystemLocale = mRes.getConfiguration().locale;
try {
startInputInnerLocked();
} catch (RuntimeException e) {
@@ -1421,34 +1440,41 @@
throw new IllegalArgumentException("Unknown id: " + id);
}
+ // See if we need to notify a subtype change within the same IME.
if (id.equals(mCurMethodId)) {
- InputMethodSubtype subtype = null;
- if (subtypeId >= 0 && subtypeId < info.getSubtypeCount()) {
- subtype = info.getSubtypeAt(subtypeId);
+ final int subtypeCount = info.getSubtypeCount();
+ if (subtypeCount <= 0) {
+ return;
}
- if (subtype != mCurrentSubtype) {
- synchronized (mMethodMap) {
- if (subtype != null) {
- setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
- }
- if (mCurMethod != null) {
- try {
- refreshImeWindowVisibilityLocked();
- // If subtype is null, try to find the most applicable one from
- // getCurrentInputMethodSubtype.
- if (subtype == null) {
- subtype = getCurrentInputMethodSubtype();
- }
- mCurMethod.changeInputMethodSubtype(subtype);
- } catch (RemoteException e) {
- return;
- }
+ final InputMethodSubtype oldSubtype = mCurrentSubtype;
+ final InputMethodSubtype newSubtype;
+ if (subtypeId >= 0 && subtypeId < subtypeCount) {
+ newSubtype = info.getSubtypeAt(subtypeId);
+ } else {
+ // If subtype is null, try to find the most applicable one from
+ // getCurrentInputMethodSubtype.
+ newSubtype = getCurrentInputMethodSubtype();
+ }
+ if (newSubtype == null || oldSubtype == null) {
+ Slog.w(TAG, "Illegal subtype state: old subtype = " + oldSubtype
+ + ", new subtype = " + newSubtype);
+ return;
+ }
+ if (newSubtype != oldSubtype) {
+ setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true);
+ if (mCurMethod != null) {
+ try {
+ refreshImeWindowVisibilityLocked();
+ mCurMethod.changeInputMethodSubtype(newSubtype);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call changeInputMethodSubtype");
}
}
}
return;
}
+ // Changing to a different IME.
final long ident = Binder.clearCallingIdentity();
try {
// Set a subtype to this input method.
@@ -2137,7 +2163,6 @@
return subtypes;
}
-
private static ArrayList<InputMethodSubtype> getOverridingImplicitlyEnabledSubtypes(
InputMethodInfo imi, String mode) {
ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
@@ -2155,15 +2180,19 @@
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
if (enabled != null && enabled.size() > 0) {
// We'd prefer to fall back on a system IME, since that is safer.
- int i=enabled.size();
+ int i = enabled.size();
+ int firstFoundSystemIme = -1;
while (i > 0) {
i--;
final InputMethodInfo imi = enabled.get(i);
- if (isSystemIme(imi) && !imi.isAuxiliaryIme()) {
- break;
+ if (isSystemImeThatHasEnglishSubtype(imi) && !imi.isAuxiliaryIme()) {
+ return imi;
+ }
+ if (firstFoundSystemIme < 0 && isSystemIme(imi) && !imi.isAuxiliaryIme()) {
+ firstFoundSystemIme = i;
}
}
- return enabled.get(i);
+ return enabled.get(Math.max(firstFoundSystemIme, 0));
}
return null;
}
@@ -2238,11 +2267,17 @@
}
}
- String defaultIme = Settings.Secure.getString(mContext
+ final String defaultImiId = Settings.Secure.getString(mContext
.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
- if (!TextUtils.isEmpty(defaultIme) && !map.containsKey(defaultIme)) {
- if (chooseNewDefaultIMELocked()) {
- updateFromSettingsLocked();
+ if (!TextUtils.isEmpty(defaultImiId)) {
+ if (!map.containsKey(defaultImiId)) {
+ Slog.w(TAG, "Default IME is uninstalled. Choose new default IME.");
+ if (chooseNewDefaultIMELocked()) {
+ updateFromSettingsLocked();
+ }
+ } else {
+ // Double check that the default IME is certainly enabled.
+ setInputMethodEnabledLocked(defaultImiId, true);
}
}
}
@@ -2626,7 +2661,8 @@
mCurrentSubtype = subtype;
} else {
mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
- mCurrentSubtype = null;
+ // If the subtype is not specified, choose the most applicable one
+ mCurrentSubtype = getCurrentInputMethodSubtype();
}
}
@@ -3007,8 +3043,8 @@
mContext = context;
mPm = context.getPackageManager();
mImms = imms;
- mSystemLocaleStr =
- imms.mLastSystemLocale != null ? imms.mLastSystemLocale.toString() : "";
+ final Locale locale = context.getResources().getConfiguration().locale;
+ mSystemLocaleStr = locale != null ? locale.toString() : "";
}
private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
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 @@
}
@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");
+ }
- final boolean changed;
synchronized (mDataStore) {
try {
- changed = mDataStore.setKeyboardLayout(
- inputDeviceDescriptor, keyboardLayoutDescriptor);
+ if (mDataStore.setCurrentKeyboardLayout(
+ inputDeviceDescriptor, keyboardLayoutDescriptor)) {
+ 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 String[] getKeyboardLayoutsForInputDevice(String inputDeviceDescriptor) {
+ if (inputDeviceDescriptor == null) {
+ throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+ }
+
+ 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 {
+ 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();
}
- reloadKeyboardLayouts();
+ }
+ }
+
+ @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();
+ }
}
}
@@ -978,12 +1208,13 @@
// 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 @@
return null;
}
- String keyboardLayoutDescriptor = getKeyboardLayoutForInputDevice(inputDeviceDescriptor);
+ String keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(
+ inputDeviceDescriptor);
if (keyboardLayoutDescriptor == null) {
return null;
}
@@ -1203,7 +1435,19 @@
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 @@
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/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 480992b..cf3a5d2 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -538,8 +538,16 @@
if (mDimAnimator == null) {
mDimAnimator = new DimAnimator(mService.mFxSession);
}
- mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
- new DimAnimator.Parameters(winAnimator, width, height, target)));
+ // Only set dim params on the highest dimmed layer.
+ final WindowStateAnimator dimWinAnimator = mDimParams == null
+ ? null : mDimParams.mDimWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed one.
+ if (winAnimator.mSurfaceShown &&
+ (dimWinAnimator == null || !dimWinAnimator.mSurfaceShown
+ || dimWinAnimator.mAnimLayer < winAnimator.mAnimLayer)) {
+ mService.mH.sendMessage(mService.mH.obtainMessage(SET_DIM_PARAMETERS,
+ new DimAnimator.Parameters(winAnimator, width, height, target)));
+ }
}
// TODO(cmautner): Move into Handler
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 @@
// 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);
}
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index 69dd666..e4cfb23 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -146,6 +146,7 @@
"ci", // Cote d'Ivoire
"eh", // Western Sahara
"fo", // Faroe Islands, Denmark
+ "gb", // United Kingdom of Great Britain and Northern Ireland
"gh", // Ghana
"gm", // Gambia
"gn", // Guinea
@@ -161,7 +162,6 @@
"sn", // Senegal
"st", // Sao Tome and Principe
"tg", // Togo
- "uk", // U.K
};
/** Reason for registration denial. */
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b9ec30c..a69adc1 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2089,6 +2089,23 @@
keep->add(rule, location);
}
+void
+addProguardKeepMethodRule(ProguardKeepSet* keep, const String8& memberName,
+ const char* pkg, const String8& srcName, int line)
+{
+ String8 rule("-keepclassmembers class * { *** ");
+ rule += memberName;
+ rule += "(...); }";
+
+ String8 location("onClick ");
+ location += srcName;
+ char lineno[20];
+ sprintf(lineno, ":%d", line);
+ location += lineno;
+
+ keep->add(rule, location);
+}
+
status_t
writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
{
@@ -2251,6 +2268,13 @@
}
}
}
+ ssize_t attrIndex = tree.indexOfAttribute(RESOURCES_ANDROID_NAMESPACE, "onClick");
+ if (attrIndex >= 0) {
+ size_t len;
+ addProguardKeepMethodRule(keep,
+ String8(tree.getAttributeStringValue(attrIndex, &len)), NULL,
+ layoutFile->getPrintableSource(), tree.getLineNumber());
+ }
}
return NO_ERROR;
@@ -2289,6 +2313,9 @@
} else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
startTag = "PreferenceScreen";
tagAttrPairs = &kXmlTagAttrPairs;
+ } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
+ startTag = "menu";
+ tagAttrPairs = NULL;
} else {
continue;
}