am b7bafba7: am 099f40a9: Support actionProviderClass attribute for menus [DO NOT MERGE]
* commit 'b7bafba7c602150da9c0f06335e3c853e919501c':
Support actionProviderClass attribute for menus [DO NOT MERGE]
diff --git a/api/current.txt b/api/current.txt
index 6b893d5..10c6e2c1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2683,6 +2683,7 @@
method public final boolean isChild();
method public boolean isDestroyed();
method public boolean isFinishing();
+ method public boolean isImmersive();
method public boolean isTaskRoot();
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public boolean moveTaskToBack(boolean);
@@ -2770,6 +2771,7 @@
method public final void setFeatureDrawableResource(int, int);
method public final void setFeatureDrawableUri(int, android.net.Uri);
method public void setFinishOnTouchOutside(boolean);
+ method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setProgress(int);
method public final void setProgressBarIndeterminate(boolean);
@@ -6330,6 +6332,7 @@
field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100
field public static final int FLAG_FINISH_ON_TASK_LAUNCH = 2; // 0x2
field public static final int FLAG_HARDWARE_ACCELERATED = 512; // 0x200
+ field public static final int FLAG_IMMERSIVE = 2048; // 0x800
field public static final int FLAG_MULTIPROCESS = 1; // 0x1
field public static final int FLAG_NO_HISTORY = 128; // 0x80
field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
@@ -23429,6 +23432,17 @@
method public void set(T, V);
}
+ public class PropertyValueModel extends android.util.ValueModel {
+ method public T get();
+ method public H getHost();
+ method public android.util.Property<H, T> getProperty();
+ method public java.lang.Class<T> getType();
+ method public static android.util.PropertyValueModel<H, T> of(H, android.util.Property<H, T>);
+ method public static android.util.PropertyValueModel<H, T> of(H, java.lang.Class<T>, java.lang.String);
+ method public static android.util.PropertyValueModel of(java.lang.Object, java.lang.String);
+ method public void set(T);
+ }
+
public class SparseArray implements java.lang.Cloneable {
ctor public SparseArray();
ctor public SparseArray(int);
@@ -23577,6 +23591,14 @@
field public int type;
}
+ public abstract class ValueModel {
+ ctor protected ValueModel();
+ method public abstract T get();
+ method public abstract java.lang.Class<T> getType();
+ method public abstract void set(T);
+ field public static final android.util.ValueModel EMPTY;
+ }
+
public class Xml {
method public static android.util.AttributeSet asAttributeSet(org.xmlpull.v1.XmlPullParser);
method public static android.util.Xml.Encoding findEncodingByName(java.lang.String) throws java.io.UnsupportedEncodingException;
@@ -27967,10 +27989,12 @@
method public abstract void onSelectedDayChange(android.widget.CalendarView, int, int, int);
}
- public class CheckBox extends android.widget.CompoundButton {
+ public class CheckBox extends android.widget.CompoundButton implements android.widget.ValueEditor {
ctor public CheckBox(android.content.Context);
ctor public CheckBox(android.content.Context, android.util.AttributeSet);
ctor public CheckBox(android.content.Context, android.util.AttributeSet, int);
+ method public android.util.ValueModel<java.lang.Boolean> getValueModel();
+ method public void setValueModel(android.util.ValueModel<java.lang.Boolean>);
}
public abstract interface Checkable {
@@ -28143,14 +28167,16 @@
method public void setSize(int, int);
}
- public class EditText extends android.widget.TextView {
+ public class EditText extends android.widget.TextView implements android.widget.ValueEditor {
ctor public EditText(android.content.Context);
ctor public EditText(android.content.Context, android.util.AttributeSet);
ctor public EditText(android.content.Context, android.util.AttributeSet, int);
method public void extendSelection(int);
+ method public android.util.ValueModel<java.lang.CharSequence> getValueModel();
method public void selectAll();
method public void setSelection(int, int);
method public void setSelection(int);
+ method public void setValueModel(android.util.ValueModel<java.lang.CharSequence>);
}
public abstract interface ExpandableListAdapter {
@@ -29175,11 +29201,13 @@
method public abstract java.lang.Object[] getSections();
}
- public class SeekBar extends android.widget.AbsSeekBar {
+ public class SeekBar extends android.widget.AbsSeekBar implements android.widget.ValueEditor {
ctor public SeekBar(android.content.Context);
ctor public SeekBar(android.content.Context, android.util.AttributeSet);
ctor public SeekBar(android.content.Context, android.util.AttributeSet, int);
+ method public android.util.ValueModel<java.lang.Integer> getValueModel();
method public void setOnSeekBarChangeListener(android.widget.SeekBar.OnSeekBarChangeListener);
+ method public void setValueModel(android.util.ValueModel<java.lang.Integer>);
}
public static abstract interface SeekBar.OnSeekBarChangeListener {
@@ -29767,6 +29795,11 @@
method public android.widget.TextView getText2();
}
+ public abstract interface ValueEditor {
+ method public abstract android.util.ValueModel<T> getValueModel();
+ method public abstract void setValueModel(android.util.ValueModel<T>);
+ }
+
public class VideoView extends android.view.SurfaceView implements android.widget.MediaController.MediaPlayerControl {
ctor public VideoView(android.content.Context);
ctor public VideoView(android.content.Context, android.util.AttributeSet);
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index 3314dc3..71e840e 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -31,6 +31,7 @@
{ AID_MEDIA, "media.player" },
{ AID_MEDIA, "media.camera" },
{ AID_MEDIA, "media.audio_policy" },
+ { AID_MEDIA, "android.media.IAAHMetaDataService" },
{ AID_DRM, "drm.drmManager" },
{ AID_NFC, "nfc" },
{ AID_BLUETOOTH, "bluetooth" },
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index b0bad07..9d6ee80 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -556,7 +556,7 @@
public IAccessibilityServiceClientWrapper(Context context, Looper looper,
Callbacks callback) {
mCallback = callback;
- mCaller = new HandlerCaller(context, looper, this);
+ mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/);
}
public void setConnection(IAccessibilityServiceConnection connection, int connectionId) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e779746..e96b475 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4818,8 +4818,8 @@
* <code>android:immersive</code> but may be changed at runtime by
* {@link #setImmersive}.
*
+ * @see #setImmersive(boolean)
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
- * @hide
*/
public boolean isImmersive() {
try {
@@ -4831,7 +4831,7 @@
/**
* Adjust the current immersive mode setting.
- *
+ *
* Note that changing this value will have no effect on the activity's
* {@link android.content.pm.ActivityInfo} structure; that is, if
* <code>android:immersive</code> is set to <code>true</code>
@@ -4840,9 +4840,8 @@
* always have its {@link android.content.pm.ActivityInfo#FLAG_IMMERSIVE
* FLAG_IMMERSIVE} bit set.
*
- * @see #isImmersive
+ * @see #isImmersive()
* @see android.content.pm.ActivityInfo#FLAG_IMMERSIVE
- * @hide
*/
public void setImmersive(boolean i) {
try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d880817..d62372c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,7 +113,6 @@
import java.util.regex.Pattern;
import libcore.io.DropBox;
-import libcore.io.EventLogger;
import libcore.io.IoUtils;
import dalvik.system.CloseGuard;
@@ -4981,13 +4980,6 @@
}
}
- private static class EventLoggingReporter implements EventLogger.Reporter {
- @Override
- public void report (int code, Object... list) {
- EventLog.writeEvent(code, list);
- }
- }
-
private class DropBoxReporter implements DropBox.Reporter {
private DropBoxManager dropBox;
@@ -5017,9 +5009,6 @@
Environment.initForCurrentUser();
- // Set the reporter for event logging in libcore
- EventLogger.setReporter(new EventLoggingReporter());
-
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index e2ca1dd..8f3b62d 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -161,7 +161,6 @@
*/
public static final int FLAG_SHOW_ON_LOCK_SCREEN = 0x0400;
/**
- * @hide
* Bit in {@link #flags} corresponding to an immersive activity
* that wishes not to be interrupted by notifications.
* Applications that hide the system notification bar with
@@ -174,7 +173,14 @@
* {@link #FLAG_IMMERSIVE} set, however, will not be interrupted; the
* notification may be shown in some other way (such as a small floating
* "toast" window).
- * {@see android.app.Notification#FLAG_HIGH_PRIORITY}
+ *
+ * Note that this flag will always reflect the Activity's
+ * <code>android:immersive</code> manifest definition, even if the Activity's
+ * immersive state is changed at runtime via
+ * {@link android.app.Activity#setImmersive(boolean)}.
+ *
+ * @see android.app.Notification#FLAG_HIGH_PRIORITY
+ * @see android.app.Activity#setImmersive(boolean)
*/
public static final int FLAG_IMMERSIVE = 0x0800;
/**
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 5324f81..d78262b 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -70,7 +70,8 @@
public IInputMethodSessionWrapper(Context context,
InputMethodSession inputMethodSession) {
- mCaller = new HandlerCaller(context, this);
+ mCaller = new HandlerCaller(context, null,
+ this, true /*asyncHandler*/);
mInputMethodSession = inputMethodSession;
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 5275314..2d67875 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -102,7 +102,8 @@
public IInputMethodWrapper(AbstractInputMethodService context,
InputMethod inputMethod) {
mTarget = new WeakReference<AbstractInputMethodService>(context);
- mCaller = new HandlerCaller(context.getApplicationContext(), this);
+ mCaller = new HandlerCaller(context.getApplicationContext(), null,
+ this, true /*asyncHandler*/);
mInputMethod = new WeakReference<InputMethod>(inputMethod);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
}
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 3a06dc0..37601fc 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -74,7 +74,7 @@
}
public void interfaceLinkStateChanged(String iface, boolean up) {
- if (mIface.equals(iface) && mLinkUp != up) {
+ if (mIface.equals(iface)) {
Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down"));
mLinkUp = up;
mTracker.mNetworkInfo.setIsAvailable(up);
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 336960e..e343e83 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -939,8 +939,9 @@
* @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
* listener should be called in the proper order (between other
* processing). May be null.
+ * @hide
*/
- void performClick(PreferenceScreen preferenceScreen) {
+ public void performClick(PreferenceScreen preferenceScreen) {
if (!isEnabled()) {
return;
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 5ca7d79..17f88f1 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -138,7 +138,10 @@
private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
- PreferenceManager(Activity activity, int firstRequestCode) {
+ /**
+ * @hide
+ */
+ public PreferenceManager(Activity activity, int firstRequestCode) {
mActivity = activity;
mNextRequestCode = firstRequestCode;
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 46f2723..1a10644 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -240,28 +240,8 @@
@Override
public ComponentName getAssistIntent(int userHandle) {
try {
- if (userHandle != UserHandle.getCallingUserId()) {
- // Requesting a different user, make sure that they have the permission
- if (ActivityManager.checkComponentPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- Binder.getCallingUid(), -1, true)
- == PackageManager.PERMISSION_GRANTED) {
- // Translate to the current user id, if caller wasn't aware
- if (userHandle == UserHandle.USER_CURRENT) {
- long identity = Binder.clearCallingIdentity();
- userHandle = ActivityManagerNative.getDefault().getCurrentUser().id;
- Binder.restoreCallingIdentity(identity);
- }
- } else {
- String msg = "Permission Denial: "
- + "Request to getAssistIntent for " + userHandle
- + " but is calling from user " + UserHandle.getCallingUserId()
- + "; this requires "
- + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
- Slog.w(TAG, msg);
- return null;
- }
- }
+ userHandle = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userHandle, true, false, "getAssistIntent", null);
IPackageManager pm = AppGlobals.getPackageManager();
Intent assistIntent = new Intent(Intent.ACTION_ASSIST);
ResolveInfo info =
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6d5705d..d1b23e4 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -961,13 +961,7 @@
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
int windowType, boolean isPreview, int reqWidth, int reqHeight) {
- if (DEBUG && mCallbackLooper != null) {
- mCallbackLooper.setMessageLogging(new LogPrinter(Log.VERBOSE, TAG));
- }
- mCaller = new HandlerCaller(context,
- mCallbackLooper != null
- ? mCallbackLooper : context.getMainLooper(),
- this);
+ mCaller = new HandlerCaller(context, context.getMainLooper(), this, true);
mConnection = conn;
mWindowToken = windowToken;
mWindowType = windowType;
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 5ef86b1..200b57b 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -411,9 +411,6 @@
* @throws android.util.TimeFormatException if s cannot be parsed.
*/
public boolean parse(String s) {
- if (s == null) {
- throw new NullPointerException("time string is null");
- }
if (nativeParse(s)) {
timezone = TIMEZONE_UTC;
return true;
diff --git a/core/java/android/util/PropertyValueModel.java b/core/java/android/util/PropertyValueModel.java
new file mode 100755
index 0000000..eb9c47d
--- /dev/null
+++ b/core/java/android/util/PropertyValueModel.java
@@ -0,0 +1,143 @@
+/*
+ * 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 android.util;
+
+/**
+ * A value model for a {@link Property property} of a host object. This class can be used for
+ * both reflective and non-reflective property implementations.
+ *
+ * @param <H> the host type, where the host is the object that holds this property
+ * @param <T> the value type
+ *
+ * @see Property
+ * @see ValueModel
+ */
+public class PropertyValueModel<H, T> extends ValueModel<T> {
+ private final H mHost;
+ private final Property<H, T> mProperty;
+
+ private PropertyValueModel(H host, Property<H, T> property) {
+ mProperty = property;
+ mHost = host;
+ }
+
+ /**
+ * Returns the host.
+ *
+ * @return the host
+ */
+ public H getHost() {
+ return mHost;
+ }
+
+ /**
+ * Returns the property.
+ *
+ * @return the property
+ */
+ public Property<H, T> getProperty() {
+ return mProperty;
+ }
+
+ @Override
+ public Class<T> getType() {
+ return mProperty.getType();
+ }
+
+ @Override
+ public T get() {
+ return mProperty.get(mHost);
+ }
+
+ @Override
+ public void set(T value) {
+ mProperty.set(mHost, value);
+ }
+
+ /**
+ * Return an appropriate PropertyValueModel for this host and property.
+ *
+ * @param host the host
+ * @param property the property
+ * @return the value model
+ */
+ public static <H, T> PropertyValueModel<H, T> of(H host, Property<H, T> property) {
+ return new PropertyValueModel<H, T>(host, property);
+ }
+
+ /**
+ * Return a PropertyValueModel for this {@code host} and a
+ * reflective property, constructed from this {@code propertyType} and {@code propertyName}.
+ *
+ * @param host
+ * @param propertyType the property type
+ * @param propertyName the property name
+ * @return a value model with this host and a reflective property with this type and name
+ *
+ * @see Property#of
+ */
+ public static <H, T> PropertyValueModel<H, T> of(H host, Class<T> propertyType,
+ String propertyName) {
+ return of(host, Property.of((Class<H>) host.getClass(), propertyType, propertyName));
+ }
+
+ private static Class getNullaryMethodReturnType(Class c, String name) {
+ try {
+ return c.getMethod(name).getReturnType();
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
+ private static Class getFieldType(Class c, String name) {
+ try {
+ return c.getField(name).getType();
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+
+ private static String capitalize(String name) {
+ if (name.isEmpty()) {
+ return name;
+ }
+ return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ }
+
+ /**
+ * Return a PropertyValueModel for this {@code host} and and {@code propertyName}.
+ *
+ * @param host the host
+ * @param propertyName the property name
+ * @return a value model with this host and a reflective property with this name
+ */
+ public static PropertyValueModel of(Object host, String propertyName) {
+ Class clazz = host.getClass();
+ String suffix = capitalize(propertyName);
+ Class propertyType = getNullaryMethodReturnType(clazz, "get" + suffix);
+ if (propertyType == null) {
+ propertyType = getNullaryMethodReturnType(clazz, "is" + suffix);
+ }
+ if (propertyType == null) {
+ propertyType = getFieldType(clazz, propertyName);
+ }
+ if (propertyType == null) {
+ throw new NoSuchPropertyException(propertyName);
+ }
+ return of(host, propertyType, propertyName);
+ }
+}
diff --git a/core/java/android/util/ValueModel.java b/core/java/android/util/ValueModel.java
new file mode 100755
index 0000000..4789682
--- /dev/null
+++ b/core/java/android/util/ValueModel.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.util;
+
+/**
+ * A ValueModel is an abstraction for a 'slot' or place in memory in which a value
+ * may be stored and retrieved. A common implementation of ValueModel is a regular property of
+ * an object, whose value may be retrieved by calling the appropriate <em>getter</em>
+ * method and set by calling the corresponding <em>setter</em> method.
+ *
+ * @param <T> the value type
+ *
+ * @see PropertyValueModel
+ */
+public abstract class ValueModel<T> {
+ /**
+ * The empty model should be used in place of {@code null} to indicate that a
+ * model has not been set. The empty model has no value and does nothing when it is set.
+ */
+ public static final ValueModel EMPTY = new ValueModel() {
+ @Override
+ public Class getType() {
+ return Object.class;
+ }
+
+ @Override
+ public Object get() {
+ return null;
+ }
+
+ @Override
+ public void set(Object value) {
+
+ }
+ };
+
+ protected ValueModel() {
+ }
+
+ /**
+ * Returns the type of this property.
+ *
+ * @return the property type
+ */
+ public abstract Class<T> getType();
+
+ /**
+ * Returns the value of this property.
+ *
+ * @return the property value
+ */
+ public abstract T get();
+
+ /**
+ * Sets the value of this property.
+ *
+ * @param value the new value for this property
+ */
+ public abstract void set(T value);
+}
\ No newline at end of file
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
new file mode 100644
index 0000000..b03e4c7
--- /dev/null
+++ b/core/java/android/view/SimulatedDpad.java
@@ -0,0 +1,298 @@
+/*
+ * 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 android.view;
+
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * This class creates DPAD events from touchpad events.
+ *
+ * @see ViewRootImpl
+ */
+
+//TODO: Make this class an internal class of ViewRootImpl.java
+class SimulatedDpad {
+
+ private static final String TAG = "SimulatedDpad";
+
+ // Maximum difference in milliseconds between the down and up of a touch
+ // event for it to be considered a tap
+ // TODO:Read this value from a configuration file
+ private static final int MAX_TAP_TIME = 250;
+ // Where the cutoff is for determining an edge swipe
+ private static final float EDGE_SWIPE_THRESHOLD = 0.9f;
+ private static final int MSG_FLICK = 313;
+ // TODO: Pass touch slop from the input device
+ private static final int TOUCH_SLOP = 30;
+ // The position of the previous touchpad event
+ private float mLastTouchpadXPosition;
+ private float mLastTouchpadYPosition;
+ // Where the touchpad was initially pressed
+ private float mTouchpadEnterXPosition;
+ private float mTouchpadEnterYPosition;
+ // When the most recent ACTION_HOVER_ENTER occurred
+ private long mLastTouchPadStartTimeMs = 0;
+ // When the most recent direction key was sent
+ private long mLastTouchPadKeySendTimeMs = 0;
+ // When the most recent touch event of any type occurred
+ private long mLastTouchPadEventTimeMs = 0;
+ // Did the swipe begin in a valid region
+ private boolean mEdgeSwipePossible;
+
+ private final Context mContext;
+
+ // How quickly keys were sent;
+ private int mKeySendRateMs = 0;
+ private int mLastKeySent;
+ // Last movement in device screen pixels
+ private float mLastMoveX = 0;
+ private float mLastMoveY = 0;
+ // Offset from the initial touch. Gets reset as direction keys are sent.
+ private float mAccumulatedX;
+ private float mAccumulatedY;
+
+ // Change in position allowed during tap events
+ private float mTouchSlop;
+ private float mTouchSlopSquared;
+ // Has the TouchSlop constraint been invalidated
+ private boolean mAlwaysInTapRegion = true;
+
+ // Information from the most recent event.
+ // Used to determine what device sent the event during a fling.
+ private int mLastSource;
+ private int mLastMetaState;
+ private int mLastDeviceId;
+
+ // TODO: Currently using screen dimensions tuned to a Galaxy Nexus, need to
+ // read this from a config file instead
+ private int mDistancePerTick;
+ private int mDistancePerTickSquared;
+ // Highest rate that the flinged events can occur at before dying out
+ private int mMaxRepeatDelay;
+ // The square of the minimum distance needed for a flick to register
+ private int mMinFlickDistanceSquared;
+ // How quickly the repeated events die off
+ private float mFlickDecay;
+
+ public SimulatedDpad(Context context) {
+ mDistancePerTick = SystemProperties.getInt("persist.vr_dist_tick", 64);
+ mDistancePerTickSquared = mDistancePerTick * mDistancePerTick;
+ mMaxRepeatDelay = SystemProperties.getInt("persist.vr_repeat_delay", 300);
+ mMinFlickDistanceSquared = SystemProperties.getInt("persist.vr_min_flick", 20);
+ mMinFlickDistanceSquared *= mMinFlickDistanceSquared;
+ mFlickDecay = Float.parseFloat(SystemProperties.get(
+ "persist.sys.vr_flick_decay", "1.3"));
+ mTouchSlop = TOUCH_SLOP;
+ mTouchSlopSquared = mTouchSlop * mTouchSlop;
+
+ mContext = context;
+ }
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FLICK: {
+ final long time = SystemClock.uptimeMillis();
+ ViewRootImpl viewroot = (ViewRootImpl) msg.obj;
+ // Send the key
+ viewroot.enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, msg.arg2, 0, mLastMetaState,
+ mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
+ viewroot.enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, msg.arg2, 0, mLastMetaState,
+ mLastDeviceId, 0, KeyEvent.FLAG_FALLBACK, mLastSource));
+
+ // Increase the delay by the decay factor and resend
+ final int delay = (int) Math.ceil(mFlickDecay * msg.arg1);
+ if (delay <= mMaxRepeatDelay) {
+ Message msgCopy = Message.obtain(msg);
+ msgCopy.arg1 = delay;
+ msgCopy.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msgCopy, delay);
+ }
+ break;
+ }
+ }
+ }
+ };
+
+ public void updateTouchPad(ViewRootImpl viewroot, MotionEvent event,
+ boolean synthesizeNewKeys) {
+ if (!synthesizeNewKeys) {
+ mHandler.removeMessages(MSG_FLICK);
+ }
+ InputDevice device = event.getDevice();
+ if (device == null) {
+ return;
+ }
+ // Store what time the touchpad event occurred
+ final long time = SystemClock.uptimeMillis();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLastTouchPadStartTimeMs = time;
+ mAlwaysInTapRegion = true;
+ mTouchpadEnterXPosition = event.getX();
+ mTouchpadEnterYPosition = event.getY();
+ mAccumulatedX = 0;
+ mAccumulatedY = 0;
+ mLastMoveX = 0;
+ mLastMoveY = 0;
+ if (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
+ * EDGE_SWIPE_THRESHOLD < event.getY()) {
+ // Did the swipe begin in a valid region
+ mEdgeSwipePossible = true;
+ }
+ // Clear any flings
+ if (synthesizeNewKeys) {
+ mHandler.removeMessages(MSG_FLICK);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Determine whether the move is slop or an intentional move
+ float deltaX = event.getX() - mTouchpadEnterXPosition;
+ float deltaY = event.getY() - mTouchpadEnterYPosition;
+ if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
+ mAlwaysInTapRegion = false;
+ }
+ // Checks if the swipe has crossed the midpoint
+ // and if our swipe gesture is complete
+ if (event.getY() < (device.getMotionRange(MotionEvent.AXIS_Y).getMax()
+ * .5) && mEdgeSwipePossible) {
+ mEdgeSwipePossible = false;
+
+ Intent intent =
+ ((SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE))
+ .getAssistIntent(mContext, UserHandle.USER_CURRENT_OR_SELF);
+ if (intent != null) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ mContext.startActivity(intent);
+ } catch (ActivityNotFoundException e){
+ Log.e(TAG, "Could not start search activity");
+ }
+ } else {
+ Log.e(TAG, "Could not find a search activity");
+ }
+ }
+ // Find the difference in position between the two most recent
+ // touchpad events
+ mLastMoveX = event.getX() - mLastTouchpadXPosition;
+ mLastMoveY = event.getY() - mLastTouchpadYPosition;
+ mAccumulatedX += mLastMoveX;
+ mAccumulatedY += mLastMoveY;
+ float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
+ float mAccumulatedYSquared = mAccumulatedY * mAccumulatedY;
+ // Determine if we've moved far enough to send a key press
+ if (mAccumulatedXSquared > mDistancePerTickSquared ||
+ mAccumulatedYSquared > mDistancePerTickSquared) {
+ float dominantAxis;
+ float sign;
+ boolean isXAxis;
+ int key;
+ int repeatCount = 0;
+ // Determine dominant axis
+ if (mAccumulatedXSquared > mAccumulatedYSquared) {
+ dominantAxis = mAccumulatedX;
+ isXAxis = true;
+ } else {
+ dominantAxis = mAccumulatedY;
+ isXAxis = false;
+ }
+ // Determine sign of axis
+ sign = (dominantAxis > 0) ? 1 : -1;
+ // Determine key to send
+ if (isXAxis) {
+ key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_RIGHT :
+ KeyEvent.KEYCODE_DPAD_LEFT;
+ } else {
+ key = (sign == 1) ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ }
+ // Send key until maximum distance constraint is satisfied
+ while (dominantAxis * dominantAxis > mDistancePerTickSquared) {
+ repeatCount++;
+ dominantAxis -= sign * mDistancePerTick;
+ if (synthesizeNewKeys) {
+ viewroot.enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, key, 0, event.getMetaState(),
+ event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
+ event.getSource()));
+ viewroot.enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, key, 0, event.getMetaState(),
+ event.getDeviceId(), 0, KeyEvent.FLAG_FALLBACK,
+ event.getSource()));
+ }
+ }
+ // Save new axis values
+ mAccumulatedX = isXAxis ? dominantAxis : 0;
+ mAccumulatedY = isXAxis ? 0 : dominantAxis;
+
+ mLastKeySent = key;
+ mKeySendRateMs = (int) ((time - mLastTouchPadKeySendTimeMs) / repeatCount);
+ mLastTouchPadKeySendTimeMs = time;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
+ if (synthesizeNewKeys) {
+ viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+ event.getMetaState(), event.getDeviceId(), 0,
+ KeyEvent.FLAG_FALLBACK, event.getSource()));
+ viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+ event.getMetaState(), event.getDeviceId(), 0,
+ KeyEvent.FLAG_FALLBACK, event.getSource()));
+ }
+ } else {
+ float xMoveSquared = mLastMoveX * mLastMoveX;
+ float yMoveSquared = mLastMoveY * mLastMoveY;
+ // Determine whether the last gesture was a fling.
+ if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
+ time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
+ mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
+ mLastDeviceId = event.getDeviceId();
+ mLastSource = event.getSource();
+ mLastMetaState = event.getMetaState();
+
+ if (synthesizeNewKeys) {
+ Message message = Message.obtain(mHandler, MSG_FLICK,
+ mKeySendRateMs, mLastKeySent, viewroot);
+ message.setAsynchronous(true);
+ mHandler.sendMessageDelayed(message, mKeySendRateMs);
+ }
+ }
+ }
+ mEdgeSwipePossible = false;
+ break;
+ }
+
+ // Store touch event position and time
+ mLastTouchPadEventTimeMs = time;
+ mLastTouchpadXPosition = event.getX();
+ mLastTouchpadYPosition = event.getY();
+ }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b6016e9..58e6fbe 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -144,6 +144,8 @@
final TrackballAxis mTrackballAxisX = new TrackballAxis();
final TrackballAxis mTrackballAxisY = new TrackballAxis();
+ final SimulatedDpad mSimulatedDpad;
+
int mLastJoystickXDirection;
int mLastJoystickYDirection;
int mLastJoystickXKeyCode;
@@ -230,6 +232,13 @@
int mLastSystemUiVisibility;
int mClientWindowLayoutFlags;
+ /** @hide */
+ public static final int EVENT_NOT_HANDLED = 0;
+ /** @hide */
+ public static final int EVENT_HANDLED = 1;
+ /** @hide */
+ public static final int EVENT_IN_PROGRESS = 2;
+
// Pool of queued input events.
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
private QueuedInputEvent mQueuedInputEventPool;
@@ -383,6 +392,7 @@
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mAttachInfo.mScreenOn = powerManager.isScreenOn();
loadSystemProperties();
+ mSimulatedDpad = new SimulatedDpad(context);
}
/**
@@ -3154,19 +3164,19 @@
return false;
}
- private void deliverInputEvent(QueuedInputEvent q) {
+ private int deliverInputEvent(QueuedInputEvent q) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
try {
if (q.mEvent instanceof KeyEvent) {
- deliverKeyEvent(q);
+ return deliverKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- deliverPointerEvent(q);
+ return deliverPointerEvent(q);
} else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- deliverTrackballEvent(q);
+ return deliverTrackballEvent(q);
} else {
- deliverGenericMotionEvent(q);
+ return deliverGenericMotionEvent(q);
}
}
} finally {
@@ -3174,7 +3184,25 @@
}
}
- private void deliverPointerEvent(QueuedInputEvent q) {
+ private int deliverInputEventPostIme(QueuedInputEvent q) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
+ try {
+ if (q.mEvent instanceof KeyEvent) {
+ return deliverKeyEventPostIme(q);
+ } else {
+ final int source = q.mEvent.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ return deliverTrackballEventPostIme(q);
+ } else {
+ return deliverGenericMotionEventPostIme(q);
+ }
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
+ private int deliverPointerEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
final boolean isTouchEvent = event.isTouchEvent();
if (mInputEventConsistencyVerifier != null) {
@@ -3187,8 +3215,7 @@
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishInputEvent(q, false);
- return;
+ return EVENT_NOT_HANDLED;
}
// Translate the pointer event for compatibility, if needed.
@@ -3221,16 +3248,10 @@
if (MEASURE_LATENCY) {
lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
}
- if (handled) {
- finishInputEvent(q, true);
- return;
- }
-
- // Pointer event was unhandled.
- finishInputEvent(q, false);
+ return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
}
- private void deliverTrackballEvent(QueuedInputEvent q) {
+ private int deliverTrackballEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
@@ -3249,24 +3270,25 @@
if (DEBUG_IMF)
Log.v(TAG, "Sending trackball event to IME: seq="
+ seq + " event=" + event);
- imm.dispatchTrackballEvent(mView.getContext(), seq, event,
+ int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
mInputMethodCallback);
- return;
+ if (result != EVENT_NOT_HANDLED) {
+ return result;
+ }
}
}
}
// Not dispatching to IME, continue with post IME actions.
- deliverTrackballEventPostIme(q);
+ return deliverTrackballEventPostIme(q);
}
- private void deliverTrackballEventPostIme(QueuedInputEvent q) {
+ private int deliverTrackballEventPostIme(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent) q.mEvent;
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
- finishInputEvent(q, false);
- return;
+ return EVENT_NOT_HANDLED;
}
// Deliver the trackball event to the view.
@@ -3276,10 +3298,8 @@
// event into a key event, touch mode will not exit, so we exit
// touch mode here.
ensureTouchMode(false);
-
- finishInputEvent(q, true);
mLastTrackballTime = Integer.MIN_VALUE;
- return;
+ return EVENT_HANDLED;
}
// Translate the trackball event into DPAD keys and try to deliver those.
@@ -3387,15 +3407,14 @@
// Unfortunately we can't tell whether the application consumed the keys, so
// we always consider the trackball event handled.
- finishInputEvent(q, true);
+ return EVENT_HANDLED;
}
- private void deliverGenericMotionEvent(QueuedInputEvent q) {
+ private int deliverGenericMotionEvent(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
}
-
if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
if (LOCAL_LOGV)
Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
@@ -3409,47 +3428,56 @@
if (DEBUG_IMF)
Log.v(TAG, "Sending generic motion event to IME: seq="
+ seq + " event=" + event);
- imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
+ int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
mInputMethodCallback);
- return;
+ if (result != EVENT_NOT_HANDLED) {
+ return result;
+ }
}
}
}
// Not dispatching to IME, continue with post IME actions.
- deliverGenericMotionEventPostIme(q);
+ return deliverGenericMotionEventPostIme(q);
}
- private void deliverGenericMotionEventPostIme(QueuedInputEvent q) {
+ private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
final MotionEvent event = (MotionEvent) q.mEvent;
- final boolean isJoystick = (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
+ final int source = event.getSource();
+ final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
+ final boolean isTouchPad = (source & InputDevice.SOURCE_CLASS_POSITION) != 0;
// If there is no view, then the event will not be handled.
if (mView == null || !mAdded) {
if (isJoystick) {
updateJoystickDirection(event, false);
+ } else if (isTouchPad) {
+ mSimulatedDpad.updateTouchPad(this, event, false);
}
- finishInputEvent(q, false);
- return;
+ return EVENT_NOT_HANDLED;
}
// Deliver the event to the view.
if (mView.dispatchGenericMotionEvent(event)) {
if (isJoystick) {
updateJoystickDirection(event, false);
+ } else if (isTouchPad) {
+ mSimulatedDpad.updateTouchPad(this, event, false);
}
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
if (isJoystick) {
// Translate the joystick event into DPAD keys and try to deliver
// those.
updateJoystickDirection(event, true);
- finishInputEvent(q, true);
- } else {
- finishInputEvent(q, false);
+ return EVENT_HANDLED;
}
+ if (isTouchPad) {
+ mSimulatedDpad.updateTouchPad(this, event, true);
+ return EVENT_HANDLED;
+ }
+ return EVENT_NOT_HANDLED;
}
private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
@@ -3593,7 +3621,7 @@
return false;
}
- private void deliverKeyEvent(QueuedInputEvent q) {
+ private int deliverKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onKeyEvent(event, 0);
@@ -3604,8 +3632,7 @@
// Perform predispatching before the IME.
if (mView.dispatchKeyEventPreIme(event)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
// Dispatch to the IME before propagating down the view hierarchy.
@@ -3616,81 +3643,30 @@
final int seq = event.getSequenceNumber();
if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+ seq + " event=" + event);
- imm.dispatchKeyEvent(mView.getContext(), seq, event, mInputMethodCallback);
- return;
+ int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
+ mInputMethodCallback);
+ if (result != EVENT_NOT_HANDLED) {
+ return result;
+ }
}
}
}
// Not dispatching to IME, continue with post IME actions.
- deliverKeyEventPostIme(q);
+ return deliverKeyEventPostIme(q);
}
- void handleImeFinishedEvent(int seq, boolean handled) {
- final QueuedInputEvent q = mCurrentInputEvent;
- if (q != null && q.mEvent.getSequenceNumber() == seq) {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + " event=" + q);
- }
- if (handled) {
- finishInputEvent(q, true);
- } else {
- if (q.mEvent instanceof KeyEvent) {
- KeyEvent event = (KeyEvent)q.mEvent;
- if (event.getAction() != KeyEvent.ACTION_UP) {
- // If the window doesn't currently have input focus, then drop
- // this event. This could be an event that came back from the
- // IME dispatch but the window has lost focus in the meantime.
- if (!mAttachInfo.mHasWindowFocus) {
- Slog.w(TAG, "Dropping event due to no window focus: " + event);
- finishInputEvent(q, true);
- return;
- }
- }
- deliverKeyEventPostIme(q);
- } else {
- MotionEvent event = (MotionEvent)q.mEvent;
- if (event.getAction() != MotionEvent.ACTION_CANCEL
- && event.getAction() != MotionEvent.ACTION_UP) {
- // If the window doesn't currently have input focus, then drop
- // this event. This could be an event that came back from the
- // IME dispatch but the window has lost focus in the meantime.
- if (!mAttachInfo.mHasWindowFocus) {
- Slog.w(TAG, "Dropping event due to no window focus: " + event);
- finishInputEvent(q, true);
- return;
- }
- }
- final int source = q.mEvent.getSource();
- if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- deliverTrackballEventPostIme(q);
- } else {
- deliverGenericMotionEventPostIme(q);
- }
- }
- }
- } else {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + ", event not found!");
- }
- }
- }
-
- private void deliverKeyEventPostIme(QueuedInputEvent q) {
+ private int deliverKeyEventPostIme(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
// If the view went away, then the event will not be handled.
if (mView == null || !mAdded) {
- finishInputEvent(q, false);
- return;
+ return EVENT_NOT_HANDLED;
}
// If the key's purpose is to exit touch mode then we consume it and consider it handled.
if (checkForLeavingTouchModeAndConsume(event)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
// Make sure the fallback event policy sees all keys that will be delivered to the
@@ -3699,8 +3675,7 @@
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
// If the Control modifier is held, try to interpret the key as a shortcut.
@@ -3709,15 +3684,13 @@
&& event.getRepeatCount() == 0
&& !KeyEvent.isModifierKey(event.getKeyCode())) {
if (mView.dispatchKeyShortcutEvent(event)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
}
// Apply the fallback event policy.
if (mFallbackEventHandler.dispatchKeyEvent(event)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
// Handle automatic focus changes.
@@ -3770,22 +3743,20 @@
if (v.requestFocus(direction, mTempRect)) {
playSoundEffect(SoundEffectConstants
.getContantForFocusDirection(direction));
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
}
// Give the focused view a last chance to handle the dpad key.
if (mView.dispatchUnhandledMove(focused, direction)) {
- finishInputEvent(q, true);
- return;
+ return EVENT_HANDLED;
}
}
}
}
// Key was unhandled.
- finishInputEvent(q, false);
+ return EVENT_NOT_HANDLED;
}
/* drag/drop */
@@ -4289,7 +4260,11 @@
mFirstPendingInputEvent = q.mNext;
q.mNext = null;
mCurrentInputEvent = q;
- deliverInputEvent(q);
+
+ final int result = deliverInputEvent(q);
+ if (result != EVENT_IN_PROGRESS) {
+ finishCurrentInputEvent(result == EVENT_HANDLED);
+ }
}
// We are done processing all input events that we can process right now
@@ -4300,10 +4275,42 @@
}
}
- private void finishInputEvent(QueuedInputEvent q, boolean handled) {
- if (q != mCurrentInputEvent) {
- throw new IllegalStateException("finished input event out of order");
+ void handleImeFinishedEvent(int seq, boolean handled) {
+ final QueuedInputEvent q = mCurrentInputEvent;
+ if (q != null && q.mEvent.getSequenceNumber() == seq) {
+ if (DEBUG_IMF) {
+ Log.v(TAG, "IME finished event: seq=" + seq
+ + " handled=" + handled + " event=" + q);
+ }
+
+ if (!handled) {
+ // If the window doesn't currently have input focus, then drop
+ // this event. This could be an event that came back from the
+ // IME dispatch but the window has lost focus in the meantime.
+ if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+ Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ } else {
+ final int result = deliverInputEventPostIme(q);
+ if (result == EVENT_HANDLED) {
+ handled = true;
+ }
+ }
+ }
+ finishCurrentInputEvent(handled);
+
+ // Immediately start processing the next input event.
+ doProcessInputEvents();
+ } else {
+ if (DEBUG_IMF) {
+ Log.v(TAG, "IME finished event: seq=" + seq
+ + " handled=" + handled + ", event not found!");
+ }
}
+ }
+
+ private void finishCurrentInputEvent(boolean handled) {
+ final QueuedInputEvent q = mCurrentInputEvent;
+ mCurrentInputEvent = null;
if (q.mReceiver != null) {
q.mReceiver.finishInputEvent(q.mEvent, handled);
@@ -4312,10 +4319,18 @@
}
recycleQueuedInputEvent(q);
+ }
- mCurrentInputEvent = null;
- if (mFirstPendingInputEvent != null) {
- scheduleProcessInputEvents();
+ private static boolean isTerminalInputEvent(InputEvent event) {
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent)event;
+ return keyEvent.getAction() == KeyEvent.ACTION_UP;
+ } else {
+ final MotionEvent motionEvent = (MotionEvent)event;
+ final int action = motionEvent.getAction();
+ return action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_CANCEL
+ || action == MotionEvent.ACTION_HOVER_EXIT;
}
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4a3f846..d258f4d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -334,7 +334,7 @@
class H extends Handler {
H(Looper looper) {
- super(looper);
+ super(looper, null, true);
}
@Override
@@ -1565,38 +1565,36 @@
/**
* @hide
*/
- public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
+ public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
FinishedEventCallback callback) {
- boolean handled = false;
synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
if (mCurMethod != null) {
if (key.getAction() == KeyEvent.ACTION_DOWN
- && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
+ && key.getKeyCode() == KeyEvent.KEYCODE_SYM
+ && key.getRepeatCount() == 0) {
showInputMethodPickerLocked();
- handled = true;
- } else {
- try {
- if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
- final long startTime = SystemClock.uptimeMillis();
- enqueuePendingEventLocked(startTime, seq, mCurId, callback);
- mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
- return;
- } catch (RemoteException e) {
- Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
- }
+ return ViewRootImpl.EVENT_HANDLED;
+ }
+ try {
+ if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
+ final long startTime = SystemClock.uptimeMillis();
+ enqueuePendingEventLocked(startTime, seq, mCurId, callback);
+ mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
+ return ViewRootImpl.EVENT_IN_PROGRESS;
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
}
}
}
-
- callback.finishedEvent(seq, handled);
+ return ViewRootImpl.EVENT_NOT_HANDLED;
}
/**
* @hide
*/
- public void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
+ public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
FinishedEventCallback callback) {
synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
@@ -1607,20 +1605,19 @@
final long startTime = SystemClock.uptimeMillis();
enqueuePendingEventLocked(startTime, seq, mCurId, callback);
mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
- return;
+ return ViewRootImpl.EVENT_IN_PROGRESS;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
}
}
}
-
- callback.finishedEvent(seq, false);
+ return ViewRootImpl.EVENT_NOT_HANDLED;
}
/**
* @hide
*/
- public void dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
+ public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
FinishedEventCallback callback) {
synchronized (mH) {
if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
@@ -1631,14 +1628,13 @@
final long startTime = SystemClock.uptimeMillis();
enqueuePendingEventLocked(startTime, seq, mCurId, callback);
mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
- return;
+ return ViewRootImpl.EVENT_IN_PROGRESS;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
}
}
}
-
- callback.finishedEvent(seq, false);
+ return ViewRootImpl.EVENT_NOT_HANDLED;
}
void finishedEvent(int seq, boolean handled) {
diff --git a/core/java/android/widget/CheckBox.java b/core/java/android/widget/CheckBox.java
index f1804f8..41ab5f2 100644
--- a/core/java/android/widget/CheckBox.java
+++ b/core/java/android/widget/CheckBox.java
@@ -20,6 +20,7 @@
import android.util.AttributeSet;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.ValueModel;
/**
@@ -55,7 +56,9 @@
* {@link android.R.styleable#View View Attributes}
* </p>
*/
-public class CheckBox extends CompoundButton {
+public class CheckBox extends CompoundButton implements ValueEditor<Boolean> {
+ private ValueModel<Boolean> mValueModel = ValueModel.EMPTY;
+
public CheckBox(Context context) {
this(context, null);
}
@@ -79,4 +82,22 @@
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(CheckBox.class.getName());
}
+
+ @Override
+ public ValueModel<Boolean> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<Boolean> valueModel) {
+ mValueModel = valueModel;
+ setChecked(mValueModel.get());
+ }
+
+ @Override
+ public boolean performClick() {
+ boolean handled = super.performClick();
+ mValueModel.set(isChecked());
+ return handled;
+ }
}
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 57e51c2..ec81214 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -17,6 +17,7 @@
package android.widget;
import android.content.Context;
+import android.graphics.Rect;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
@@ -24,6 +25,7 @@
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
+import android.util.ValueModel;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -47,7 +49,9 @@
* {@link android.R.styleable#TextView TextView Attributes},
* {@link android.R.styleable#View View Attributes}
*/
-public class EditText extends TextView {
+public class EditText extends TextView implements ValueEditor<CharSequence> {
+ private ValueModel<CharSequence> mValueModel = ValueModel.EMPTY;
+
public EditText(Context context) {
this(context, null);
}
@@ -128,4 +132,21 @@
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(EditText.class.getName());
}
+
+ @Override
+ public ValueModel<CharSequence> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<CharSequence> valueModel) {
+ mValueModel = valueModel;
+ setText(mValueModel.get());
+ }
+
+ @Override
+ void sendAfterTextChanged(Editable text) {
+ super.sendAfterTextChanged(text);
+ mValueModel.set(text);
+ }
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 87396fb..26c801f 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -551,7 +551,10 @@
applied to the drawable, be sure to call setImageMatrix().
*/
public Matrix getImageMatrix() {
- return mMatrix;
+ if (mDrawMatrix == null) {
+ return Matrix.IDENTITY_MATRIX;
+ }
+ return mDrawMatrix;
}
public void setImageMatrix(Matrix matrix) {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index ac21671..74ded18 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -470,6 +470,11 @@
private final PressedStateHelper mPressedStateHelper;
/**
+ * The keycode of the last handled DPAD down event.
+ */
+ private int mLastHandledDownDpadKeyCode = -1;
+
+ /**
* Interface to listen for changes of the current value.
*/
public interface OnValueChangeListener {
@@ -936,6 +941,31 @@
case KeyEvent.KEYCODE_ENTER:
removeAllCallbacks();
break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (!mHasSelectorWheel) {
+ break;
+ }
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN)
+ ? getValue() < getMaxValue() : getValue() > getMinValue()) {
+ requestFocus();
+ mLastHandledDownDpadKeyCode = keyCode;
+ removeAllCallbacks();
+ if (mFlingScroller.isFinished()) {
+ changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN);
+ }
+ return true;
+ }
+ break;
+ case KeyEvent.ACTION_UP:
+ if (mLastHandledDownDpadKeyCode == keyCode) {
+ mLastHandledDownDpadKeyCode = -1;
+ return true;
+ }
+ break;
+ }
}
return super.dispatchKeyEvent(event);
}
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index 2737f94..a6486a8 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.util.ValueModel;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -33,7 +34,7 @@
*
* @attr ref android.R.styleable#SeekBar_thumb
*/
-public class SeekBar extends AbsSeekBar {
+public class SeekBar extends AbsSeekBar implements ValueEditor<Integer> {
/**
* A callback that notifies clients when the progress level has been
@@ -69,8 +70,9 @@
void onStopTrackingTouch(SeekBar seekBar);
}
+ private ValueModel<Integer> mValueModel = ValueModel.EMPTY;
private OnSeekBarChangeListener mOnSeekBarChangeListener;
-
+
public SeekBar(Context context) {
this(context, null);
}
@@ -89,9 +91,23 @@
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
+ if (fromUser) {
+ mValueModel.set(getProgress());
+ }
}
}
+ @Override
+ public ValueModel<Integer> getValueModel() {
+ return mValueModel;
+ }
+
+ @Override
+ public void setValueModel(ValueModel<Integer> valueModel) {
+ mValueModel = valueModel;
+ setProgress(mValueModel.get());
+ }
+
/**
* Sets a listener to receive notifications of changes to the SeekBar's progress level. Also
* provides notifications of when the user starts and stops a touch gesture within the SeekBar.
diff --git a/core/java/android/widget/ValueEditor.java b/core/java/android/widget/ValueEditor.java
new file mode 100755
index 0000000..2b91abf
--- /dev/null
+++ b/core/java/android/widget/ValueEditor.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.widget;
+
+import android.util.ValueModel;
+
+/**
+ * An interface for editors of simple values. Classes implementing this interface are normally
+ * UI controls (subclasses of {@link android.view.View View}) that can provide a suitable
+ * user interface to display and edit values of the specified type. This interface is
+ * intended to describe editors for simple types, like {@code boolean}, {@code int} or
+ * {@code String}, where the values themselves are immutable.
+ * <p>
+ * For example, {@link android.widget.CheckBox CheckBox} implements
+ * this interface for the Boolean type as it is capable of providing an appropriate
+ * mechanism for displaying and changing the value of a Boolean property.
+ *
+ * @param <T> the value type that this editor supports
+ */
+public interface ValueEditor<T> {
+ /**
+ * Return the last value model that was set. If no value model has been set, the editor
+ * should return the value {@link android.util.ValueModel#EMPTY}.
+ *
+ * @return the value model
+ */
+ public ValueModel<T> getValueModel();
+
+ /**
+ * Sets the value model for this editor. When the value model is set, the editor should
+ * retrieve the value from the value model, using {@link android.util.ValueModel#get()},
+ * and set its internal state accordingly. Likewise, when the editor's internal state changes
+ * it should update the value model by calling {@link android.util.ValueModel#set(T)}
+ * with the appropriate value.
+ *
+ * @param valueModel the new value model for this editor.
+ */
+ public void setValueModel(ValueModel<T> valueModel);
+}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 84699dc..b442ff5 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -24,38 +24,32 @@
public class HandlerCaller {
public final Context mContext;
-
+
final Looper mMainLooper;
final Handler mH;
final Callback mCallback;
class MyHandler extends Handler {
- MyHandler(Looper looper) {
- super(looper);
+ MyHandler(Looper looper, boolean async) {
+ super(looper, null, async);
}
-
+
@Override
public void handleMessage(Message msg) {
mCallback.executeMessage(msg);
}
}
-
+
public interface Callback {
public void executeMessage(Message msg);
}
-
- public HandlerCaller(Context context, Callback callback) {
- mContext = context;
- mMainLooper = context.getMainLooper();
- mH = new MyHandler(mMainLooper);
- mCallback = callback;
- }
- public HandlerCaller(Context context, Looper looper, Callback callback) {
+ public HandlerCaller(Context context, Looper looper, Callback callback,
+ boolean asyncHandler) {
mContext = context;
- mMainLooper = looper;
- mH = new MyHandler(mMainLooper);
+ mMainLooper = looper != null ? looper : context.getMainLooper();
+ mH = new MyHandler(mMainLooper, asyncHandler);
mCallback = callback;
}
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index aa2c5f39..0a59ae7 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -23,7 +23,6 @@
#include "jni.h"
#include "utils/misc.h"
#include "android_runtime/AndroidRuntime.h"
-#include "ScopedStringChars.h"
#include "TimeUtils.h"
#include <nativehelper/JNIHelp.h>
#include <cutils/tztime.h>
@@ -72,10 +71,11 @@
t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
bool allDay = env->GetBooleanField(o, g_allDayField);
if (allDay &&
- ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "allDay is true but sec, min, hour are not 0.");
- return false;
+ ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
+ char msg[100];
+ sprintf(msg, "allDay is true but sec, min, hour are not 0.");
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg);
+ return false;
}
return true;
}
@@ -313,7 +313,7 @@
static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
{
Time t;
- if (!java2time(env, &t, This)) return env->NewStringUTF("");
+ if (!java2time(env, &t, This)) return env->NewStringUTF("");;
ACQUIRE_TIMEZONE(This, t)
String8 r = t.toString();
@@ -365,30 +365,32 @@
// ============================================================================
// Just do this here because it's not worth recreating the strings
-static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
- bool* thrown)
+static int get_char(JNIEnv* env, const jchar *s, int spos, int mul,
+ bool *thrown)
{
jchar c = s[spos];
if (c >= '0' && c <= '9') {
return (c - '0') * mul;
} else {
if (!*thrown) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Parse error at pos=%d", spos);
+ char msg[100];
+ sprintf(msg, "Parse error at pos=%d", spos);
+ jniThrowException(env, "android/util/TimeFormatException", msg);
*thrown = true;
}
return 0;
}
}
-static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
+static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected)
{
jchar c = s[spos];
if (c != expected) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at pos=%d. Expected %c.",
- c, spos, expected);
- return false;
+ char msg[100];
+ sprintf(msg, "Unexpected character 0x%02x at pos=%d. Expected %c.", c, spos,
+ expected);
+ jniThrowException(env, "android/util/TimeFormatException", msg);
+ return false;
}
return true;
}
@@ -397,19 +399,20 @@
static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
{
jsize len = env->GetStringLength(strObj);
- if (len < 8) {
- jniThrowException(env, "android/util/TimeFormatException",
- "String too short -- expected at least 8 characters.");
- return false;
- }
+ const jchar *s = env->GetStringChars(strObj, NULL);
+ bool thrown = false;
+ int n;
jboolean inUtc = false;
- ScopedStringChars s(env, strObj);
+ if (len < 8) {
+ char msg[100];
+ sprintf(msg, "String too short -- expected at least 8 characters.");
+ jniThrowException(env, "android/util/TimeFormatException", msg);
+ return false;
+ }
// year
- int n;
- bool thrown = false;
n = get_char(env, s, 0, 1000, &thrown);
n += get_char(env, s, 1, 100, &thrown);
n += get_char(env, s, 2, 10, &thrown);
@@ -456,7 +459,7 @@
if (len > 15) {
// Z
if (!check_char(env, s, 15, 'Z')) return false;
- inUtc = true;
+ inUtc = true;
}
} else {
env->SetBooleanField(This, g_allDayField, JNI_TRUE);
@@ -469,7 +472,8 @@
env->SetIntField(This, g_ydayField, 0);
env->SetIntField(This, g_isdstField, -1);
env->SetLongField(This, g_gmtoffField, 0);
-
+
+ env->ReleaseStringChars(strObj, s);
return inUtc;
}
@@ -478,19 +482,19 @@
jstring strObj)
{
jsize len = env->GetStringLength(strObj);
+ const jchar *s = env->GetStringChars(strObj, NULL);
+
+ bool thrown = false;
+ int n;
+ jboolean inUtc = false;
+
if (len < 10) {
jniThrowException(env, "android/util/TimeFormatException",
- "String too short --- expected at least 10 characters.");
+ "Time input is too short; must be at least 10 characters");
return false;
}
- jboolean inUtc = false;
-
- ScopedStringChars s(env, strObj);
-
// year
- int n;
- bool thrown = false;
n = get_char(env, s, 0, 1000, &thrown);
n += get_char(env, s, 1, 100, &thrown);
n += get_char(env, s, 2, 10, &thrown);
@@ -521,28 +525,28 @@
// T
if (!check_char(env, s, 10, 'T')) return false;
- env->SetBooleanField(This, g_allDayField, JNI_FALSE);
+ env->SetBooleanField(This, g_allDayField, JNI_FALSE);
// hour
n = get_char(env, s, 11, 10, &thrown);
n += get_char(env, s, 12, 1, &thrown);
if (thrown) return false;
- int hour = n;
+ int hour = n;
// env->SetIntField(This, g_hourField, n);
+
+ // :
+ if (!check_char(env, s, 13, ':')) return false;
- // :
- if (!check_char(env, s, 13, ':')) return false;
-
- // minute
+ // minute
n = get_char(env, s, 14, 10, &thrown);
n += get_char(env, s, 15, 1, &thrown);
if (thrown) return false;
- int minute = n;
+ int minute = n;
// env->SetIntField(This, g_minField, n);
- // :
- if (!check_char(env, s, 16, ':')) return false;
+ // :
+ if (!check_char(env, s, 16, ':')) return false;
- // second
+ // second
n = get_char(env, s, 17, 10, &thrown);
n += get_char(env, s, 18, 1, &thrown);
if (thrown) return false;
@@ -562,63 +566,64 @@
if (len > tz_index) {
char c = s[tz_index];
- // NOTE: the offset is meant to be subtracted to get from local time
- // to UTC. we therefore use 1 for '-' and -1 for '+'.
- switch (c) {
- case 'Z':
- // Zulu time -- UTC
- offset = 0;
- break;
- case '-':
+ // NOTE: the offset is meant to be subtracted to get from local time
+ // to UTC. we therefore use 1 for '-' and -1 for '+'.
+ switch (c) {
+ case 'Z':
+ // Zulu time -- UTC
+ offset = 0;
+ break;
+ case '-':
offset = 1;
- break;
- case '+':
+ break;
+ case '+':
offset = -1;
- break;
- default:
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected character 0x%02x at position %d. Expected + or -",
- c, tz_index);
- return false;
- }
+ break;
+ default:
+ char msg[100];
+ sprintf(msg, "Unexpected character 0x%02x at position %d. Expected + or -",
+ c, tz_index);
+ jniThrowException(env, "android/util/TimeFormatException", msg);
+ return false;
+ }
inUtc = true;
- if (offset != 0) {
- if (len < tz_index + 6) {
- jniThrowExceptionFmt(env, "android/util/TimeFormatException",
- "Unexpected length; should be %d characters",
- tz_index + 6);
- return false;
- }
+ if (offset != 0) {
+ if (len < tz_index + 6) {
+ char msg[100];
+ sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6);
+ jniThrowException(env, "android/util/TimeFormatException", msg);
+ return false;
+ }
- // hour
- n = get_char(env, s, tz_index + 1, 10, &thrown);
- n += get_char(env, s, tz_index + 2, 1, &thrown);
- if (thrown) return false;
- n *= offset;
- hour += n;
+ // hour
+ n = get_char(env, s, tz_index + 1, 10, &thrown);
+ n += get_char(env, s, tz_index + 2, 1, &thrown);
+ if (thrown) return false;
+ n *= offset;
+ hour += n;
- // :
- if (!check_char(env, s, tz_index + 3, ':')) return false;
-
- // minute
- n = get_char(env, s, tz_index + 4, 10, &thrown);
- n += get_char(env, s, tz_index + 5, 1, &thrown);
- if (thrown) return false;
- n *= offset;
- minute += n;
- }
- }
- env->SetIntField(This, g_hourField, hour);
+ // :
+ if (!check_char(env, s, tz_index + 3, ':')) return false;
+
+ // minute
+ n = get_char(env, s, tz_index + 4, 10, &thrown);
+ n += get_char(env, s, tz_index + 5, 1, &thrown);
+ if (thrown) return false;
+ n *= offset;
+ minute += n;
+ }
+ }
+ env->SetIntField(This, g_hourField, hour);
env->SetIntField(This, g_minField, minute);
- if (offset != 0) {
- // we need to normalize after applying the hour and minute offsets
- android_text_format_Time_normalize(env, This, false /* use isdst */);
- // The timezone is set to UTC in the calling Java code.
- }
+ if (offset != 0) {
+ // we need to normalize after applying the hour and minute offsets
+ android_text_format_Time_normalize(env, This, false /* use isdst */);
+ // The timezone is set to UTC in the calling Java code.
+ }
} else {
- env->SetBooleanField(This, g_allDayField, JNI_TRUE);
+ env->SetBooleanField(This, g_allDayField, JNI_TRUE);
env->SetIntField(This, g_hourField, 0);
env->SetIntField(This, g_minField, 0);
env->SetIntField(This, g_secField, 0);
@@ -628,7 +633,8 @@
env->SetIntField(This, g_ydayField, 0);
env->SetIntField(This, g_isdstField, -1);
env->SetLongField(This, g_gmtoffField, 0);
-
+
+ env->ReleaseStringChars(strObj, s);
return inUtc;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2a357af..bee9a8c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -714,6 +714,12 @@
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="signature|system" />
+ <!-- Allows access to the loop radio (Android@Home mesh network) device.
+ @hide -->
+ <permission android:name="android.permission.LOOP_RADIO"
+ android:permissionGroup="android.permission-group.NETWORK"
+ android:protectionLevel="signature|system" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 83ecdd9..cf04b5c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -106,6 +106,10 @@
<group gid="net_bw_acct" />
</permission>
+ <permission name="android.permission.LOOP_RADIO" >
+ <group gid="loop_radio" />
+ </permission>
+
<!-- ================================================================== -->
<!-- ================================================================== -->
<!-- ================================================================== -->
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 94e0bd4..a296d34 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -602,12 +602,12 @@
mHandler = new Handler(this);
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
- mCaller = new HandlerCaller(context, new HandlerCaller.Callback() {
+ mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() {
@Override
public void executeMessage(Message msg) {
handleMessage(msg);
}
- });
+ }, true /*asyncHandler*/);
mWindowManagerService = windowManager;
mHardKeyboardListener = new HardKeyboardListener();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 1d08d31..6e81e9d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -115,6 +115,7 @@
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UpdateLock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.format.Time;
@@ -191,6 +192,7 @@
static final boolean DEBUG_POWER = localLOGV || false;
static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
static final boolean DEBUG_MU = localLOGV || false;
+ static final boolean DEBUG_IMMERSIVE = localLOGV || false;
static final boolean VALIDATE_TOKENS = false;
static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -827,6 +829,12 @@
long mLastWriteTime = 0;
/**
+ * Used to retain an update lock when the foreground activity is in
+ * immersive mode.
+ */
+ final UpdateLock mUpdateLock = new UpdateLock("immersive");
+
+ /**
* Set to true after the system has finished booting.
*/
boolean mBooted = false;
@@ -895,6 +903,7 @@
static final int REPORT_USER_SWITCH_MSG = 34;
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
+ static final int IMMERSIVE_MODE_LOCK_MSG = 37;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1356,6 +1365,21 @@
timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
break;
}
+ case IMMERSIVE_MODE_LOCK_MSG: {
+ final boolean nextState = (msg.arg1 != 0);
+ if (mUpdateLock.isHeld() != nextState) {
+ if (DEBUG_IMMERSIVE) {
+ final ActivityRecord r = (ActivityRecord) msg.obj;
+ Slog.d(TAG, "Applying new update lock state '" + nextState + "' for " + r);
+ }
+ if (nextState) {
+ mUpdateLock.acquire();
+ } else {
+ mUpdateLock.release();
+ }
+ }
+ break;
+ }
}
}
};
@@ -1822,9 +1846,20 @@
if (r != null) {
mWindowManager.setFocusedApp(r.appToken, true);
}
+ applyUpdateLockStateLocked(r);
}
}
+ final void applyUpdateLockStateLocked(ActivityRecord r) {
+ // Modifications to the UpdateLock state are done on our handler, outside
+ // the activity manager's locks. The new state is determined based on the
+ // state *now* of the relevant activity record. The object is passed to
+ // the handler solely for logging detail, not to be consulted/modified.
+ final boolean nextState = r != null && r.immersive;
+ mHandler.sendMessage(
+ mHandler.obtainMessage(IMMERSIVE_MODE_LOCK_MSG, (nextState) ? 1 : 0, 0, r));
+ }
+
private final void updateLruProcessInternalLocked(ProcessRecord app, int bestPos) {
// put it on the LRU to keep track of when it should be exited.
int lrui = mLruProcesses.indexOf(app);
@@ -7421,11 +7456,19 @@
public void setImmersive(IBinder token, boolean immersive) {
synchronized(this) {
- ActivityRecord r = mMainStack.isInStackLocked(token);
+ final ActivityRecord r = mMainStack.isInStackLocked(token);
if (r == null) {
throw new IllegalArgumentException();
}
r.immersive = immersive;
+
+ // update associated state if we're frontmost
+ if (r == mFocusedActivity) {
+ if (DEBUG_IMMERSIVE) {
+ Slog.d(TAG, "Frontmost changed immersion: "+ r);
+ }
+ applyUpdateLockStateLocked(r);
+ }
}
}
diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java
similarity index 100%
rename from services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
rename to services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java
diff --git a/services/java/com/android/server/updates/CertPinInstallReceiver.java b/services/java/com/android/server/updates/CertPinInstallReceiver.java
deleted file mode 100644
index c03fbc3..0000000
--- a/services/java/com/android/server/updates/CertPinInstallReceiver.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.updates;
-
-public class CertPinInstallReceiver extends ConfigUpdateInstallReceiver {
-
- public CertPinInstallReceiver() {
- super("/data/misc/keychain/", "pins", "metadata/", "version");
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java b/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java
deleted file mode 100644
index b6742a1..0000000
--- a/services/tests/servicestests/src/com/android/server/updates/CertPinInstallReceiverTest.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * 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.updates;
-
-import android.content.Context;
-import android.content.Intent;
-import android.test.AndroidTestCase;
-import android.provider.Settings;
-import android.util.Base64;
-import android.util.Log;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.cert.CertificateFactory;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.Signature;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.KeyFactory;
-import java.util.HashSet;
-import java.io.*;
-import libcore.io.IoUtils;
-
-/**
- * Tests for {@link com.android.server.CertPinInstallReceiver}
- */
-public class CertPinInstallReceiverTest extends AndroidTestCase {
-
- private static final String TAG = "CertPinInstallReceiverTest";
-
- private static final String PINLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
-
- public static final String PINLIST_CONTENT_PATH = PINLIST_ROOT + "pins";
- public static final String PINLIST_METADATA_PATH = PINLIST_CONTENT_PATH + "metadata";
-
- public static final String PINLIST_CONTENT_URL_KEY = "pinlist_content_url";
- public static final String PINLIST_METADATA_URL_KEY = "pinlist_metadata_url";
- public static final String PINLIST_CERTIFICATE_KEY = "config_update_certificate";
- public static final String PINLIST_VERSION_KEY = "pinlist_version";
-
- private static final String EXTRA_CONTENT_PATH = "CONTENT_PATH";
- private static final String EXTRA_REQUIRED_HASH = "REQUIRED_HASH";
- private static final String EXTRA_SIGNATURE = "SIGNATURE";
- private static final String EXTRA_VERSION_NUMBER = "VERSION";
-
- public static final String TEST_CERT = "" +
- "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET" +
- "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v" +
- "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN" +
- "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB" +
- "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp" +
- "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv" +
- "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA" +
- "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq" +
- "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG" +
- "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W" +
- "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx" +
- "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3" +
- "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k" +
- "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF" +
- "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR" +
- "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK" +
- "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM=";
-
-
- public static final String TEST_KEY = "" +
- "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKMYYcBMFiWZqz7SwQQKSejwtnUo" +
- "+CVv+Z97pWkJPcaFKv7tuvs0b9nAZmcSEuoTtggfh/GW0eZALdPP6MzVg36QxpCBFGOj8sEIl04p" +
- "ozBRuRXKcAfo5iwp6+Os4+TyV8zlIVDMkwawD35N+imvnGDQHBnW8D/4TeYTtOvczZS/AgMBAAEC" +
- "gYBxwFalNSwZK3WJipq+g6KLCiBn1JxGGDQlLKrweFaSuFyFky9fd3IvkIabirqQchD612sMb+GT" +
- "0t1jptW6z4w2w6++IW0A3apDOCwoD+uvDBXrbFqI0VbyAWUNqHVdaFFIRk2IHGEE6463mGRdmILX" +
- "IlCd/85RTHReg4rl/GFqWQJBANgLAIR4pWbl5Gm+DtY18wp6Q3pJAAMkmP/lISCBIidu1zcqYIKt" +
- "PoDW4Knq9xnhxPbXrXKv4YzZWHBK8GkKhQ0CQQDBQnXufQcMew+PwiS0oJvS+eQ6YJwynuqG2ejg" +
- "WE+T7489jKtscRATpUXpZUYmDLGg9bLt7L62hFvFSj2LO2X7AkBcdrD9AWnBFWlh/G77LVHczSEu" +
- "KCoyLiqxcs5vy/TjLaQ8vw1ZQG580/qJnr+tOxyCjSJ18GK3VppsTRaBznfNAkB3nuCKNp9HTWCL" +
- "dfrsRsFMrFpk++mSt6SoxXaMbn0LL2u1CD4PCEiQMGt+lK3/3TmRTKNs+23sYS7Ahjxj0udDAkEA" +
- "p57Nj65WNaWeYiOfTwKXkLj8l29H5NbaGWxPT0XkWr4PvBOFZVH/wj0/qc3CMVGnv11+DyO+QUCN" +
- "SqBB5aRe8g==";
-
- private void overrideSettings(String key, String value) throws Exception {
- assertTrue(Settings.Secure.putString(mContext.getContentResolver(), key, value));
- Thread.sleep(1000);
- }
-
- private void overrideCert(String value) throws Exception {
- overrideSettings(PINLIST_CERTIFICATE_KEY, value);
- }
-
- private String readPins() throws Exception {
- return IoUtils.readFileAsString(PINLIST_CONTENT_PATH);
- }
-
- private String readCurrentVersion() throws Exception {
- return IoUtils.readFileAsString("/data/misc/keychain/metadata/version");
- }
-
- private String getNextVersion() throws Exception {
- int currentVersion = Integer.parseInt(readCurrentVersion());
- return Integer.toString(currentVersion + 1);
- }
-
- private static String getCurrentHash(String content) throws Exception {
- if (content == null) {
- return "0";
- }
- MessageDigest dgst = MessageDigest.getInstance("SHA512");
- byte[] encoded = content.getBytes();
- byte[] fingerprint = dgst.digest(encoded);
- return IntegralToString.bytesToHexString(fingerprint, false);
- }
-
- private static String getHashOfCurrentContent() throws Exception {
- String content = IoUtils.readFileAsString("/data/misc/keychain/pins");
- return getCurrentHash(content);
- }
-
- private PrivateKey createKey() throws Exception {
- byte[] derKey = Base64.decode(TEST_KEY.getBytes(), Base64.DEFAULT);
- PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(derKey);
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
- return (PrivateKey) keyFactory.generatePrivate(keySpec);
- }
-
- private X509Certificate createCertificate() throws Exception {
- byte[] derCert = Base64.decode(TEST_CERT.getBytes(), Base64.DEFAULT);
- InputStream istream = new ByteArrayInputStream(derCert);
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- return (X509Certificate) cf.generateCertificate(istream);
- }
-
- private String makeTemporaryContentFile(String content) throws Exception {
- FileOutputStream fw = mContext.openFileOutput("content.txt", mContext.MODE_WORLD_READABLE);
- fw.write(content.getBytes(), 0, content.length());
- fw.close();
- return mContext.getFilesDir() + "/content.txt";
- }
-
- private String createSignature(String content, String version, String requiredHash)
- throws Exception {
- Signature signer = Signature.getInstance("SHA512withRSA");
- signer.initSign(createKey());
- signer.update(content.trim().getBytes());
- signer.update(version.trim().getBytes());
- signer.update(requiredHash.getBytes());
- String sig = new String(Base64.encode(signer.sign(), Base64.DEFAULT));
- assertEquals(true,
- verifySignature(content, version, requiredHash, sig, createCertificate()));
- return sig;
- }
-
- public boolean verifySignature(String content, String version, String requiredPrevious,
- String signature, X509Certificate cert) throws Exception {
- Signature signer = Signature.getInstance("SHA512withRSA");
- signer.initVerify(cert);
- signer.update(content.trim().getBytes());
- signer.update(version.trim().getBytes());
- signer.update(requiredPrevious.trim().getBytes());
- return signer.verify(Base64.decode(signature.getBytes(), Base64.DEFAULT));
- }
-
- private void sendIntent(String contentPath, String version, String required, String sig) {
- Intent i = new Intent();
- i.setAction("android.intent.action.UPDATE_PINS");
- i.putExtra(EXTRA_CONTENT_PATH, contentPath);
- i.putExtra(EXTRA_VERSION_NUMBER, version);
- i.putExtra(EXTRA_REQUIRED_HASH, required);
- i.putExtra(EXTRA_SIGNATURE, sig);
- mContext.sendBroadcast(i);
- }
-
- private String runTest(String cert, String content, String version, String required, String sig)
- throws Exception {
- Log.e(TAG, "started test");
- overrideCert(cert);
- String contentPath = makeTemporaryContentFile(content);
- sendIntent(contentPath, version, required, sig);
- Thread.sleep(1000);
- return readPins();
- }
-
- private String runTestWithoutSig(String cert, String content, String version, String required)
- throws Exception {
- String sig = createSignature(content, version, required);
- return runTest(cert, content, version, required, sig);
- }
-
- public void testOverwritePinlist() throws Exception {
- Log.e(TAG, "started testOverwritePinList");
- assertEquals("abcde", runTestWithoutSig(TEST_CERT, "abcde", getNextVersion(), getHashOfCurrentContent()));
- Log.e(TAG, "started testOverwritePinList");
- }
-
- public void testBadSignatureFails() throws Exception {
- Log.e(TAG, "started testOverwritePinList");
- String text = "blahblah";
- runTestWithoutSig(TEST_CERT, text, getNextVersion(), getHashOfCurrentContent());
- assertEquals(text, runTest(TEST_CERT, "bcdef", getNextVersion(), getCurrentHash(text), ""));
- Log.e(TAG, "started testOverwritePinList");
- }
-
- public void testBadRequiredHashFails() throws Exception {
- runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent());
- assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", getNextVersion(), "0"));
- Log.e(TAG, "started testOverwritePinList");
- }
-
- public void testBadVersionFails() throws Exception {
- String text = "blahblahblahblah";
- String version = getNextVersion();
- runTestWithoutSig(TEST_CERT, text, version, getHashOfCurrentContent());
- assertEquals(text, runTestWithoutSig(TEST_CERT, "defgh", version, getCurrentHash(text)));
- Log.e(TAG, "started testOverwritePinList");
- }
-
- public void testOverrideRequiredHash() throws Exception {
- runTestWithoutSig(TEST_CERT, "blahblahblah", getNextVersion(), getHashOfCurrentContent());
- assertEquals("blahblahblah", runTestWithoutSig(TEST_CERT, "cdefg", "NONE", "0"));
- Log.e(TAG, "started testOverwritePinList");
- }
-
-}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index c3a0930..f7083a9 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1840,25 +1840,6 @@
return NO_ERROR;
}
-/*
- * Do PNG Crunching on a single flag
- * -i points to a single png file
- * -o points to a single png output file
- */
-int doSingleCrunch(Bundle* bundle)
-{
- fprintf(stdout, "Crunching single PNG file: %s\n", bundle->getSingleCrunchInputFile());
- fprintf(stdout, "\tOutput file: %s\n", bundle->getSingleCrunchOutputFile());
-
- String8 input(bundle->getSingleCrunchInputFile());
- String8 output(bundle->getSingleCrunchOutputFile());
- if (preProcessImageToCache(bundle, input, output) != NO_ERROR) {
- // we can't return the status_t as it gets truncate to the lower 8 bits.
- return 42;
- }
- return NO_ERROR;
-}
-
char CONSOLE_DATA[2925] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32,
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 77168f9..9c2e1b9 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1946,7 +1946,7 @@
const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
fprintf(fp,
- "int styleable %s_%s %d\n",
+ "int styleable.%s_%s %d\n",
nclassName.string(),
String8(name).string(), (int)pos);
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 039319d..4a4320c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -30,6 +30,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.IConnectivityManager;
import android.net.ConnectivityManager;
@@ -68,6 +69,7 @@
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -1949,6 +1951,26 @@
break;
}
+ if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_APPLIANCE) ==
+ Configuration.UI_MODE_TYPE_APPLIANCE) {
+ // For appliance devices, add a key listener which accepts.
+ dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
+
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
+ // TODO: make the actual key come from a config value.
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ sendMessage(PEER_CONNECTION_USER_ACCEPT);
+ dialog.dismiss();
+ return true;
+ }
+ return false;
+ }
+ });
+ // TODO: add timeout for this dialog.
+ // TODO: update UI in appliance mode to tell user what to do.
+ }
+
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}