diff options
56 files changed, 1605 insertions, 796 deletions
diff --git a/api/current.txt b/api/current.txt index f21a4f3b721d..5053859bc69c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3371,6 +3371,7 @@ package android.app { method public void send(android.content.Context, int, android.content.Intent) throws android.app.PendingIntent.CanceledException; method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException; + method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException; method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -3452,6 +3453,7 @@ package android.app { field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id"; field public static final java.lang.String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + field public static final java.lang.String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint"; field public static final java.lang.String SUGGEST_COLUMN_QUERY = "suggest_intent_query"; field public static final java.lang.String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id"; field public static final java.lang.String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing"; @@ -5297,6 +5299,7 @@ package android.content { method public java.lang.String getTargetPackage(); method public static android.content.IntentSender readIntentSenderOrNullFromParcel(android.os.Parcel); method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler) throws android.content.IntentSender.SendIntentException; + method public void sendIntent(android.content.Context, int, android.content.Intent, android.content.IntentSender.OnFinished, android.os.Handler, java.lang.String) throws android.content.IntentSender.SendIntentException; method public static void writeIntentSenderOrNullToParcel(android.content.IntentSender, android.os.Parcel); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; @@ -22261,6 +22264,7 @@ package android.view { method public static deprecated int getTouchSlop(); method public static deprecated int getWindowTouchSlop(); method public static long getZoomControlsTimeout(); + method public boolean hasPermanentMenuKey(); } public class ViewDebug { @@ -22395,6 +22399,7 @@ package android.view { method public void requestDisallowInterceptTouchEvent(boolean); method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent); method public void requestTransparentRegion(android.view.View); + method protected void resetLayoutDirectionResolution(); method public void scheduleLayoutAnimation(); method public void setAddStatesFromChildren(boolean); method public void setAlwaysDrawnWithCacheEnabled(boolean); @@ -26063,6 +26068,7 @@ package android.widget { method protected void onTextChanged(java.lang.CharSequence, int, int, int); method public boolean onTextContextMenuItem(int); method public void removeTextChangedListener(android.text.TextWatcher); + method protected void resetLayoutDirectionResolution(); method public final void setAutoLinkMask(int); method public void setCompoundDrawablePadding(int); method public void setCompoundDrawables(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable, android.graphics.drawable.Drawable); diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 164acbcdf71f..68c992671f97 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -31,82 +31,151 @@ import android.view.accessibility.AccessibilityNodeInfo; * An accessibility service runs in the background and receives callbacks by the system * when {@link AccessibilityEvent}s are fired. Such events denote some state transition * in the user interface, for example, the focus has changed, a button has been clicked, - * etc. + * etc. Such a service can optionally request the capability for querying the content + * of the active window. Development of an accessibility service requires extends this + * class and implements its abstract methods. * <p> - * An accessibility service extends this class and implements its abstract methods. Such - * a service is declared as any other service in an AndroidManifest.xml but it must also - * specify that it handles the "android.accessibilityservice.AccessibilityService" - * {@link android.content.Intent}. Following is an example of such a declaration: - * <p> - * <code> - * <service android:name=".MyAccessibilityService"><br> - * <intent-filter><br> - * <action android:name="android.accessibilityservice.AccessibilityService" /><br> - * </intent-filter><br> - * </service><br> - * </code> + * <strong>Lifecycle</strong> * </p> * <p> - * The lifecycle of an accessibility service is managed exclusively by the system. Starting - * or stopping an accessibility service is triggered by an explicit user action through + * The lifecycle of an accessibility service is managed exclusively by the system and + * follows the established service life cycle. Additionally, starting or stopping an + * accessibility service is triggered exclusively by an explicit user action through * enabling or disabling it in the device settings. After the system binds to a service it * calls {@link AccessibilityService#onServiceConnected()}. This method can be * overriden by clients that want to perform post binding setup. * </p> * <p> + * <strong>Declaration</strong> + * </p> + * <p> + * An accessibility is declared as any other service in an AndroidManifest.xml but it + * must also specify that it handles the "android.accessibilityservice.AccessibilityService" + * {@link android.content.Intent}. Failure to declare this intent will cause the system to + * ignore the accessibility service. Following is an example declaration: + * </p> + * <p> + * <code> + * <pre> + * <service android:name=".MyAccessibilityService"> + * <intent-filter> + * <action android:name="android.accessibilityservice.AccessibilityService" /> + * </intent-filter> + * . . . + * </service> + * </pre> + * </code> + * </p> + * <p> + * <strong>Configuration</strong> + * </p> + * <p> * An accessibility service can be configured to receive specific types of accessibility events, * listen only to specific packages, get events from each type only once in a given time frame, * retrieve window content, specify a settings activity, etc. * </p> + * <p> * There are two approaches for configuring an accessibility service: + * </p> * <ul> - * <li> - * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring - * the service. A service declaration with a meta-data tag is presented below: - * <p> - * <code> - * <service android:name=".MyAccessibilityService"><br> - * <intent-filter><br> - * <action android:name="android.accessibilityservice.AccessibilityService" /><br> - * </intent-filter><br> - * <meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /><br> - * </service><br> - * </code> - * </p> - * <p> - * <strong> - * This approach enables setting all accessibility service properties. - * </strong> - * </p> - * <p> - * For more details refer to {@link #SERVICE_META_DATA}. - * </p> - * </li> - * <li> - * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note - * that this method can be called any time to change the service configuration.<br> - * <p> - * <strong> - * This approach enables setting only dynamically configurable accessibility - * service properties: - * {@link AccessibilityServiceInfo#eventTypes}, - * {@link AccessibilityServiceInfo#feedbackType}, - * {@link AccessibilityServiceInfo#flags}, - * {@link AccessibilityServiceInfo#notificationTimeout}, - * {@link AccessibilityServiceInfo#packageNames} - * </strong> - * </p> - * <p> - * For more details refer to {@link AccessibilityServiceInfo}. - * </p> - * </li> + * <li> + * Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring + * the service. A service declaration with a meta-data tag is presented below: + * <p> + * <code> + * <pre> + * <service android:name=".MyAccessibilityService"> + * <intent-filter> + * <action android:name="android.accessibilityservice.AccessibilityService" /> + * </intent-filter> + * <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice" /> + * </service> + * </pre> + * </code> + * </p> + * <p> + * <strong>Note:</strong>This approach enables setting all properties. + * </p> + * <p> + * For more details refer to {@link #SERVICE_META_DATA} and + * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code>.. + * </p> + * </li> + * <li> + * Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note + * that this method can be called any time to dynamically change the service configuration. + * <p> + * <strong>Note:</strong> This approach enables setting only dynamically configurable properties: + * {@link AccessibilityServiceInfo#eventTypes}, + * {@link AccessibilityServiceInfo#feedbackType}, + * {@link AccessibilityServiceInfo#flags}, + * {@link AccessibilityServiceInfo#notificationTimeout}, + * {@link AccessibilityServiceInfo#packageNames} + * </p> + * <p> + * For more details refer to {@link AccessibilityServiceInfo}. + * </p> + * </li> * </ul> * <p> - * An accessibility service can be registered for events in specific packages to provide a - * specific type of feedback and is notified with a certain timeout after the last event - * of interest has been fired. + * <strong>Retrieving window content</strong> + * </p> + * <p> + * An service can specify in its declaration that it can retrieve the active window + * content which is represented as a tree of {@link AccessibilityNodeInfo}. Note that + * declaring this capability requires that the service declares its configuration via + * an XML resource referenced by {@link #SERVICE_META_DATA}. + * </p> + * <p> + * For security purposes an accessibility service can retrieve only the content of the + * currently active window. The currently active window is defined as the window from + * which was fired the last event of the following types: + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, + * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, + * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, + * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, + * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}, + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED}, + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}, + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}. + * In other words, the active window is the one where the user interaction is taking place. + * </p> + * <p> + * The entry point for retrieving window content is through calling + * {@link AccessibilityEvent#getSource() AccessibilityEvent.getSource()} of the last received + * event of the above types or a previous event from the same window + * (see {@link AccessibilityEvent#getWindowId() AccessibilityEvent.getWindowId()}). Invoking + * this method will return an {@link AccessibilityNodeInfo} that can be used to traverse the + * window content which represented as a tree of such objects. + * </p> + * <p> + * <strong>Note</strong>An accessibility service may have requested to be notified for + * a subset of the event types, thus be unaware that the active window has changed. Therefore + * accessibility service that would like to retrieve window content should: + * <ul> + * <li> + * Register for all event types with no notification timeout and keep track for the active + * window by calling {@link AccessibilityEvent#getWindowId()} of the last received event and + * compare this with the {@link AccessibilityNodeInfo#getWindowId()} before calling retrieval + * methods on the latter. + * </li> + * <li> + * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail since the + * active window has changed and the service did not get the accessibility event yet. Note + * that it is possible to have a retrieval method failing event adopting the strategy + * specified in the previous bullet because the accessibility event dispatch is asynchronous + * and crosses process boundaries. + * </li> + * </ul> + * </p> * <p> * <b>Notification strategy</b> + * </p> * <p> * For each feedback type only one accessibility service is notified. Services are notified * in the order of registration. Hence, if two services are registered for the same @@ -117,9 +186,10 @@ import android.view.accessibility.AccessibilityNodeInfo; * registration order. This enables "generic" accessibility services that work reasonably * well with most applications to coexist with "polished" ones that are targeted for * specific applications. + * </p> * <p> * <b>Event types</b> - * <p> + * </p> * {@link AccessibilityEvent#TYPE_VIEW_CLICKED} * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED} * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED} @@ -127,9 +197,16 @@ import android.view.accessibility.AccessibilityNodeInfo; * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED} * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED} - * <p> - * <b>Feedback types</b> - * <p> + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START} + * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER} + * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT} + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} + * {@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} + * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} + * <p> + * <b>Feedback types</b> + * <p> * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC} * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE} @@ -140,10 +217,10 @@ import android.view.accessibility.AccessibilityNodeInfo; * @see AccessibilityServiceInfo * @see android.view.accessibility.AccessibilityManager * - * Note: The event notification timeout is useful to avoid propagating events to the client - * too frequently since this is accomplished via an expensive interprocess call. - * One can think of the timeout as a criteria to determine when event generation has - * settled down. + * <strong>Note:</strong> The event notification timeout is useful to avoid propagating + * events to the client too frequently since this is accomplished via an expensive + * interprocess call. One can think of the timeout as a criteria to determine when + * event generation has settled down. */ public abstract class AccessibilityService extends Service { /** @@ -154,57 +231,25 @@ public abstract class AccessibilityService extends Service { /** * Name under which an AccessibilityService component publishes information - * about itself. This meta-data must reference an XML resource containing - * an + * about itself. This meta-data must reference an XML resource containing an * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> * tag. This is a a sample XML file configuring an accessibility service: * <p> * <code> - * <?xml version="1.0" encoding="utf-8"?><br> - * <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br> - * android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br> - * android:packageNames="foo.bar, foo.baz"<br> - * android:accessibilityFeedbackType="feedbackSpoken"<br> - * android:notificationTimeout="100"<br> - * android:accessibilityFlags="flagDefault"<br> - * android:settingsActivity="foo.bar.TestBackActivity"<br> - * . . .<br> + * <pre> + * <accessibility-service + * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" + * android:packageNames="foo.bar, foo.baz" + * android:accessibilityFeedbackType="feedbackSpoken" + * android:notificationTimeout="100" + * android:accessibilityFlags="flagDefault" + * android:settingsActivity="foo.bar.TestBackActivity" + * android:canRetrieveWindowContent="true" + * . . . * /> + * </pre> * </code> * </p> - * <p> - * <strong>Note:</strong> A service can retrieve only the content of the active window. - * An active window is the source of the most recent event of type - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}, - * {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}, - * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}, - * {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}, - * {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}, - * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}, - * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}, - * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}, - * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}. - * Therefore the service should: - * <ul> - * <li> - * Register for all event types with no notification timeout and keep track - * for the active window by calling - * {@link AccessibilityEvent#getWindowId()} of the last received - * event and compare this with the - * {@link AccessibilityNodeInfo#getWindowId()} before calling - * retrieval methods on the latter. - * </li> - * <li> - * Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail - * since the active window has changed and the service did not get the - * accessibility event. Note that it is possible to have a retrieval method - * failing event adopting the strategy specified in the previous bullet - * because the accessibility event dispatch is asynchronous and crosses - * process boundaries. - * </li> - * <ul> - * </p> */ public static final String SERVICE_META_DATA = "android.accessibilityservice"; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index b9878cd9c97a..ef4adca97a82 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -37,13 +37,13 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** - * This class describes an {@link AccessibilityService}. The system - * notifies an {@link AccessibilityService} for - * {@link android.view.accessibility.AccessibilityEvent}s + * This class describes an {@link AccessibilityService}. The system notifies an + * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s * according to the information encapsulated in this class. * * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent + * @see android.view.accessibility.AccessibilityManager */ public class AccessibilityServiceInfo implements Parcelable { @@ -93,12 +93,19 @@ public class AccessibilityServiceInfo implements Parcelable { * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START + * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED + * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED + * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED */ public int eventTypes; /** * The package names an {@link AccessibilityService} is interested in. Setting - * to null is equivalent to all packages. + * to <code>null</code> is equivalent to all packages. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> @@ -125,10 +132,10 @@ public class AccessibilityServiceInfo implements Parcelable { * <strong>Can be dynamically set at runtime.</strong>. * </p> * <p> - * Note: The event notification timeout is useful to avoid propagating events to the client - * too frequently since this is accomplished via an expensive interprocess call. - * One can think of the timeout as a criteria to determine when event generation has - * settled down + * <strong>Note:</strong> The event notification timeout is useful to avoid propagating + * events to the client too frequently since this is accomplished via an expensive + * interprocess call. One can think of the timeout as a criteria to determine when + * event generation has settled down. */ public long notificationTimeout; @@ -159,7 +166,7 @@ public class AccessibilityServiceInfo implements Parcelable { private String mSettingsActivityName; /** - * Flag whether this accessibility service can retrieve screen content. + * Flag whether this accessibility service can retrieve window content. */ private boolean mCanRetrieveWindowContent; @@ -296,12 +303,12 @@ public class AccessibilityServiceInfo implements Parcelable { } /** - * Whether this service can retrieve the currently focused window content. + * Whether this service can retrieve the current window's content. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> - * @return True screen content is retrieved. + * @return True window content can be retrieved. */ public boolean getCanRetrieveWindowContent() { return mCanRetrieveWindowContent; diff --git a/core/java/android/accessibilityservice/package.html b/core/java/android/accessibilityservice/package.html new file mode 100644 index 000000000000..0c640d14368f --- /dev/null +++ b/core/java/android/accessibilityservice/package.html @@ -0,0 +1,22 @@ +<html> +<body> +<p> + The classes in this package are used for development of accessibility service that + provide alternative or augmented feedback to the user. +</p> +<p> + An {@link android.accessibilityservice.AccessibilityService} runs in the background and + receives callbacks by the system when {@link android.view.accessibility.AccessibilityEvent}s + are fired. Such events denote some state transition in the user interface, for example, the + focus has changed, a button has been clicked, etc. Such a service can optionally request the + capability for querying the content of the active window. Development of an accessibility + service requires extends this class and implements its abstract methods. +</p> +<p> + An {@link android.accessibilityservice.AccessibilityServiceInfo} describes an + {@link android.accessibilityservice.AccessibilityService}. The system notifies an + AccessibilityService for {@link android.view.accessibility.AccessibilityEvent}s + according to the information encapsulated in this class. +</p> +</body> +</html> diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 85f40c92963a..fdf4a3af906d 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -251,12 +251,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM IBinder b = data.readStrongBinder(); IApplicationThread app = b != null ? ApplicationThreadNative.asInterface(b) : null; + String packageName = data.readString(); b = data.readStrongBinder(); IIntentReceiver rec = b != null ? IIntentReceiver.Stub.asInterface(b) : null; IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data); String perm = data.readString(); - Intent intent = registerReceiver(app, rec, filter, perm); + Intent intent = registerReceiver(app, packageName, rec, filter, perm); reply.writeNoException(); if (intent != null) { reply.writeInt(1); @@ -1503,6 +1504,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IIntentSender r = IIntentSender.Stub.asInterface( + data.readStrongBinder()); + boolean res = isIntentSenderTargetedToPackage(r); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -1702,7 +1713,7 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String packageName, IIntentReceiver receiver, IntentFilter filter, String perm) throws RemoteException { @@ -1710,6 +1721,7 @@ class ActivityManagerProxy implements IActivityManager Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller != null ? caller.asBinder() : null); + data.writeString(packageName); data.writeStrongBinder(receiver != null ? receiver.asBinder() : null); filter.writeToParcel(data, 0); data.writeString(perm); @@ -3385,5 +3397,18 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(sender.asBinder()); + mRemote.transact(IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + data.recycle(); + reply.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 94a4afaf5629..8749d3e17e0e 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -61,7 +61,6 @@ import android.net.wifi.IWifiManager; import android.net.wifi.WifiManager; import android.nfc.NfcManager; import android.os.Binder; -import android.os.Build; import android.os.Bundle; import android.os.DropBoxManager; import android.os.Environment; @@ -81,7 +80,6 @@ import android.content.ClipboardManager; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.ContextThemeWrapper; -import android.view.Display; import android.view.WindowManagerImpl; import android.view.accessibility.AccessibilityManager; import android.view.inputmethod.InputMethodManager; @@ -142,6 +140,7 @@ class ContextImpl extends Context { new HashMap<String, SharedPreferencesImpl>(); /*package*/ LoadedApk mPackageInfo; + private String mBasePackageName; private Resources mResources; /*package*/ ActivityThread mMainThread; private Context mOuterContext; @@ -1030,7 +1029,7 @@ class ContextImpl extends Context { } try { return ActivityManagerNative.getDefault().registerReceiver( - mMainThread.getApplicationThread(), + mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission); } catch (RemoteException e) { return null; @@ -1397,7 +1396,7 @@ class ContextImpl extends Context { if (pi != null) { ContextImpl c = new ContextImpl(); c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED; - c.init(pi, null, mMainThread, mResources); + c.init(pi, null, mMainThread, mResources, mBasePackageName); if (c.mResources != null) { return c; } @@ -1450,6 +1449,7 @@ class ContextImpl extends Context { */ public ContextImpl(ContextImpl context) { mPackageInfo = context.mPackageInfo; + mBasePackageName = context.mBasePackageName; mResources = context.mResources; mMainThread = context.mMainThread; mContentResolver = context.mContentResolver; @@ -1458,13 +1458,14 @@ class ContextImpl extends Context { final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) { - init(packageInfo, activityToken, mainThread, null); + init(packageInfo, activityToken, mainThread, null, null); } final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread, - Resources container) { + Resources container, String basePackageName) { mPackageInfo = packageInfo; + mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName; mResources = mPackageInfo.getResources(mainThread); if (mResources != null && container != null @@ -1485,6 +1486,7 @@ class ContextImpl extends Context { final void init(Resources resources, ActivityThread mainThread) { mPackageInfo = null; + mBasePackageName = null; mResources = resources; mMainThread = mainThread; mContentResolver = new ApplicationContentResolver(this, mainThread); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index e2588cfb5eb1..9e207646a9a2 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -103,7 +103,7 @@ public interface IActivityManager extends IInterface { throws RemoteException; public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException; public boolean willActivityBeVisible(IBinder token) throws RemoteException; - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String requiredPermission) throws RemoteException; public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException; @@ -361,6 +361,8 @@ public interface IActivityManager extends IInterface { public void registerProcessObserver(IProcessObserver observer) throws RemoteException; public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException; + public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -587,4 +589,5 @@ public interface IActivityManager extends IInterface { int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131; int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132; int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133; + int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134; } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 5b43b650abb1..b4827cbb7a65 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -365,7 +365,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send() throws CanceledException { - send(null, 0, null, null, null); + send(null, 0, null, null, null, null); } /** @@ -379,7 +379,7 @@ public final class PendingIntent implements Parcelable { * is no longer allowing more intents to be sent through it. */ public void send(int code) throws CanceledException { - send(null, code, null, null, null); + send(null, code, null, null, null, null); } /** @@ -399,7 +399,7 @@ public final class PendingIntent implements Parcelable { */ public void send(Context context, int code, Intent intent) throws CanceledException { - send(context, code, intent, null, null); + send(context, code, intent, null, null, null); } /** @@ -420,7 +420,7 @@ public final class PendingIntent implements Parcelable { */ public void send(int code, OnFinished onFinished, Handler handler) throws CanceledException { - send(null, code, null, onFinished, handler); + send(null, code, null, onFinished, handler, null); } /** @@ -449,20 +449,64 @@ public final class PendingIntent implements Parcelable { * @see #send(int) * @see #send(Context, int, Intent) * @see #send(int, android.app.PendingIntent.OnFinished, Handler) + * @see #send(Context, int, Intent, OnFinished, Handler, String) * * @throws CanceledException Throws CanceledException if the PendingIntent * is no longer allowing more intents to be sent through it. */ public void send(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws CanceledException { + send(context, code, intent, onFinished, handler, null); + } + + /** + * Perform the operation associated with this PendingIntent, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * <p>For the intent parameter, a PendingIntent + * often has restrictions on which fields can be supplied here, based on + * how the PendingIntent was retrieved in {@link #getActivity}, + * {@link #getBroadcast}, or {@link #getService}. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the PendingIntent's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * + * @see #send() + * @see #send(int) + * @see #send(Context, int, Intent) + * @see #send(int, android.app.PendingIntent.OnFinished, Handler) + * @see #send(Context, int, Intent, OnFinished, Handler) + * + * @throws CanceledException Throws CanceledException if the PendingIntent + * is no longer allowing more intents to be sent through it. + */ + public void send(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler, String requiredPermission) + throws CanceledException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; int res = mTarget.send(code, intent, resolvedType, onFinished != null - ? new FinishedDispatcher(this, onFinished, handler) - : null); + ? new FinishedDispatcher(this, onFinished, handler) + : null, + requiredPermission); if (res < 0) { throw new CanceledException(); } @@ -491,6 +535,20 @@ public final class PendingIntent implements Parcelable { } /** + * @hide + * Check to verify that this PendingIntent targets a specific package. + */ + public boolean isTargetedToPackage() { + try { + return ActivityManagerNative.getDefault() + .isIntentSenderTargetedToPackage(mTarget); + } catch (RemoteException e) { + // Should never happen. + return false; + } + } + + /** * Comparison operator on two PendingIntent objects, such that true * is returned then they both represent the same operation from the * same package. This allows you to use {@link #getActivity}, diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 85a2fa831f65..7274362e77b5 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -329,6 +329,15 @@ public class SearchManager public final static String SUGGEST_COLUMN_FLAGS = "suggest_flags"; /** + * Column name for suggestions cursor. <i>Optional.</i> This column may be + * used to specify the time in (@link System#currentTimeMillis + * System.currentTImeMillis()} (wall time in UTC) when an item was last + * accessed within the results-providing application. If set, this may be + * used to show more-recently-used items first. + */ + public final static String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint"; + + /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion * should not be stored as a shortcut in global search. */ diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index aecec66b9f30..fed6d815c9f2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1029,6 +1029,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.</p> + * * <p class="note">Note: this method <em>cannot be called from a * {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver * that is declared in an application's manifest. It is okay, however, to call @@ -1059,6 +1065,12 @@ public abstract class Context { * * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts. * + * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers + * registered with this method will correctly respect the + * {@link Intent#setPackage(String)} specified for an Intent being broadcast. + * Prior to that, it would be ignored and delivered to all matching registered + * receivers. Be careful if using this for security.</p> + * * @param receiver The BroadcastReceiver to handle the broadcast. * @param filter Selects the Intent broadcasts to be received. * @param broadcastPermission String naming a permissions that a diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl index b7da47219ce4..7dbd6f2ab92d 100644 --- a/core/java/android/content/IIntentSender.aidl +++ b/core/java/android/content/IIntentSender.aidl @@ -22,5 +22,5 @@ import android.content.Intent; /** @hide */ interface IIntentSender { int send(int code, in Intent intent, String resolvedType, - IIntentReceiver finishedReceiver); + IIntentReceiver finishedReceiver, String requiredPermission); } diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index 007a71515214..4db4bdca3657 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -154,14 +154,47 @@ public class IntentSender implements Parcelable { */ public void sendIntent(Context context, int code, Intent intent, OnFinished onFinished, Handler handler) throws SendIntentException { + sendIntent(context, code, intent, onFinished, handler, null); + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * @param requiredPermission Name of permission that a recipient of the PendingIntent + * is required to hold. This is only valid for broadcast intents, and + * corresponds to the permission argument in + * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}. + * If null, no permission is required. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler, String requiredPermission) + throws SendIntentException { try { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; int res = mTarget.send(code, intent, resolvedType, onFinished != null - ? new FinishedDispatcher(this, onFinished, handler) - : null); + ? new FinishedDispatcher(this, onFinished, handler) + : null, + requiredPermission); if (res < 0) { throw new SendIntentException(); } diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 5ade9eb9fbc5..0eb8cd81bcdd 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -338,7 +338,15 @@ public final class NdefRecord implements Parcelable { * @hide */ public static NdefRecord createUri(Uri uri) { - String uriString = uri.toString(); + return createUri(uri.toString()); + } + + /** + * Creates an NDEF record of well known type URI. + * TODO: Make a public API + * @hide + */ + public static NdefRecord createUri(String uriString) { byte prefix = 0x0; for (int i = 1; i < URI_PREFIX_MAP.length; i++) { if (uriString.startsWith(URI_PREFIX_MAP[i])) { diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 54583d61f4f9..a73067a5f709 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -313,14 +313,16 @@ public final class Tag implements Parcelable { */ @Override public String toString() { - StringBuilder sb = new StringBuilder("TAG ") - .append("uid = ") - .append(mId) - .append(" Tech ["); - for (int i : mTechList) { - sb.append(i) - .append(", "); + StringBuilder sb = new StringBuilder("TAG: Tech ["); + String[] techList = getTechList(); + int length = techList.length; + for (int i = 0; i < length; i++) { + sb.append(techList[i]); + if (i < length - 1) { + sb.append(", "); + } } + sb.append("]"); return sb.toString(); } diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 3971045f3451..b492615a50c2 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -17,9 +17,6 @@ package android.provider; -import com.android.internal.util.ArrayUtils; - -import android.accounts.Account; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentProviderClient; @@ -35,13 +32,10 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; -import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; import android.util.Log; -import java.util.Arrays; - /** * <p> * The contract between the calendar provider and applications. Contains @@ -97,6 +91,8 @@ public final class CalendarContract { /** * Broadcast Action: This is the intent that gets fired when an alarm * notification needs to be posted for a reminder. + * + * @SdkConstant */ public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; @@ -122,8 +118,7 @@ public final class CalendarContract { /** * The content:// style URL for the top-level calendar authority */ - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY); + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); /** * An optional insert, update or delete URI parameter that allows the caller @@ -572,29 +567,6 @@ public final class CalendarContract { * </ul> */ public static class Calendars implements BaseColumns, SyncColumns, CalendarColumns { - private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?" - + " AND " - + Calendars.ACCOUNT_TYPE + "=?"; - - /** - * Helper function for generating a calendars query. This is blocking - * and should not be used on the UI thread. See - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * for more details about using the parameters. - * - * @param cr The ContentResolver to query with - * @param projection A list of columns to return - * @param selection A formatted selection string - * @param selectionArgs arguments to the selection string - * @param orderBy How to order the returned rows - * @return - */ - public static final Cursor query(ContentResolver cr, String[] projection, String selection, - String[] selectionArgs, String orderBy) { - return cr.query(CONTENT_URI, projection, selection, selectionArgs, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - /** * The content:// style URL for accessing Calendars */ @@ -622,7 +594,9 @@ public final class CalendarContract { * These fields are only writable by a sync adapter. To modify them the * caller must include {@link #CALLER_IS_SYNCADAPTER}, * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the Uri's query - * parameters. + * parameters. TODO move to provider + * + * @hide */ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, @@ -737,7 +711,7 @@ public final class CalendarContract { /** * the projection used by the attendees query */ - private static final String[] PROJECTION = new String[] { + public static final String[] PROJECTION = new String[] { _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,}; private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?"; @@ -1420,37 +1394,6 @@ public final class CalendarContract { CalendarColumns { /** - * Queries all events with the given projection. This is a blocking call - * and should not be done on the UI thread. - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @return A Cursor containing all events in the db - */ - public static final Cursor query(ContentResolver cr, String[] projection) { - return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); - } - - /** - * Queries events using the given projection, selection filter, and - * ordering. This is a blocking call and should not be done on the UI - * thread. For selection and selectionArgs usage see - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param orderBy How to order the rows as an SQL ORDER BY statement - * @return A Cursor containing the matching events - */ - public static final Cursor query(ContentResolver cr, String[] projection, String selection, - String[] selectionArgs, String orderBy) { - return cr.query(CONTENT_URI, projection, selection, null, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** * The content:// style URL for interacting with events. Appending an * event id using {@link ContentUris#withAppendedId(Uri, long)} will * specify a single event. @@ -1464,7 +1407,7 @@ public final class CalendarContract { * appended event ID. Deletion of exceptions requires both the original event ID and * the exception event ID (see {@link Uri.Builder#appendPath}). */ - public static final Uri EXCEPTION_CONTENT_URI = + public static final Uri CONTENT_EXCEPTION_URI = Uri.parse("content://" + AUTHORITY + "/exception"); /** @@ -1475,7 +1418,9 @@ public final class CalendarContract { /** * These are columns that should only ever be updated by the provider, * either because they are views mapped to another table or because they - * are used for provider only functionality. + * are used for provider only functionality. TODO move to provider + * + * @hide */ public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] { ACCOUNT_NAME, @@ -1505,7 +1450,9 @@ public final class CalendarContract { /** * These fields are only writable by a sync adapter. To modify them the * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and - * _SYNC_ACCOUNT_TYPE in the query parameters. + * _SYNC_ACCOUNT_TYPE in the query parameters. TODO move to provider. + * + * @hide */ public static final String[] SYNC_WRITABLE_COLUMNS = new String[] { _SYNC_ID, @@ -1672,11 +1619,6 @@ public final class CalendarContract { public static final String END_MINUTE = "endMinute"; } - /** - * CalendarCache stores some settings for calendar including the current - * time zone for the instaces. These settings are stored using a key/value - * scheme. - */ protected interface CalendarCacheColumns { /** * The key for the setting. Keys are defined in {@link CalendarCache}. @@ -1689,6 +1631,11 @@ public final class CalendarContract { public static final String VALUE = "value"; } + /** + * CalendarCache stores some settings for calendar including the current + * time zone for the instances. These settings are stored using a key/value + * scheme. A {@link #KEY} must be specified when updating these values. + */ public static class CalendarCache implements CalendarCacheColumns { /** * The URI to use for retrieving the properties from the Calendar db. @@ -1697,22 +1644,11 @@ public final class CalendarContract { Uri.parse("content://" + AUTHORITY + "/properties"); /** - * If updating a property, this must be provided as the selection. All - * other selections will fail. For queries this field can be omitted to - * retrieve all properties or used to query a single property. Valid - * keys include {@link #TIMEZONE_KEY_TYPE}, - * {@link #TIMEZONE_KEY_INSTANCES}, and - * {@link #TIMEZONE_KEY_INSTANCES_PREVIOUS}, though the last one can - * only be read, not written. - */ - public static final String WHERE = "key=?"; - - /** * They key for updating the use of auto/home time zones in Calendar. * Valid values are {@link #TIMEZONE_TYPE_AUTO} or * {@link #TIMEZONE_TYPE_HOME}. */ - public static final String TIMEZONE_KEY_TYPE = "timezoneType"; + public static final String KEY_TIMEZONE_TYPE = "timezoneType"; /** * The key for updating the time zone used by the provider when it @@ -1720,24 +1656,24 @@ public final class CalendarContract { * type is set to {@link #TIMEZONE_TYPE_HOME}. A valid time zone id * should be written to this field. */ - public static final String TIMEZONE_KEY_INSTANCES = "timezoneInstances"; + public static final String KEY_TIMEZONE_INSTANCES = "timezoneInstances"; /** * The key for reading the last time zone set by the user. This should * only be read by apps and it will be automatically updated whenever - * {@link #TIMEZONE_KEY_INSTANCES} is updated with + * {@link #KEY_TIMEZONE_INSTANCES} is updated with * {@link #TIMEZONE_TYPE_HOME} set. */ - public static final String TIMEZONE_KEY_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; + public static final String KEY_TIMEZONE_INSTANCES_PREVIOUS = "timezoneInstancesPrevious"; /** - * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider * should stay in sync with the device's time zone. */ public static final String TIMEZONE_TYPE_AUTO = "auto"; /** - * The value to write to {@link #TIMEZONE_KEY_TYPE} if the provider + * The value to write to {@link #KEY_TIMEZONE_TYPE} if the provider * should use a fixed time zone set by the user. */ public static final String TIMEZONE_TYPE_HOME = "home"; @@ -1814,7 +1750,7 @@ public final class CalendarContract { /** * The projection used by the EventDays query. */ - private static final String[] PROJECTION = { + public static final String[] PROJECTION = { STARTDAY, ENDDAY }; private static final String SELECTION = "selected=1"; @@ -1900,7 +1836,7 @@ public final class CalendarContract { /** * The projection used by the reminders query. */ - private static final String[] PROJECTION = new String[] { + public static final String[] PROJECTION = new String[] { _ID, MINUTES, METHOD,}; @SuppressWarnings("hiding") public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders"); @@ -1967,17 +1903,28 @@ public final class CalendarContract { public static final String NOTIFY_TIME = "notifyTime"; /** - * The state of this alert. It starts out as {@link #SCHEDULED}, then - * when the alarm goes off, it changes to {@link #FIRED}, and then when - * the user dismisses the alarm it changes to {@link #DISMISSED}. Column + * The state of this alert. It starts out as {@link #STATE_SCHEDULED}, then + * when the alarm goes off, it changes to {@link #STATE_FIRED}, and then when + * the user dismisses the alarm it changes to {@link #STATE_DISMISSED}. Column * name. * <P>Type: INTEGER</P> */ public static final String STATE = "state"; - public static final int SCHEDULED = 0; - public static final int FIRED = 1; - public static final int DISMISSED = 2; + /** + * An alert begins in this state when it is first created. + */ + public static final int STATE_SCHEDULED = 0; + /** + * After a notification for an alert has been created it should be + * updated to fired. + */ + public static final int STATE_FIRED = 1; + /** + * Once the user has dismissed the notification the alert's state should + * be set to dismissed so it is not fired again. + */ + public static final int STATE_DISMISSED = 2; /** * The number of minutes that this alarm precedes the start time. Column @@ -2024,7 +1971,7 @@ public final class CalendarContract { private static final String WHERE_FINDNEXTALARMTIME = ALARM_TIME + ">=?"; private static final String SORT_ORDER_ALARMTIME_ASC = ALARM_TIME + " ASC"; - private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + SCHEDULED + private static final String WHERE_RESCHEDULE_MISSED_ALARMS = STATE + "=" + STATE_SCHEDULED + " AND " + ALARM_TIME + "<?" + " AND " + ALARM_TIME + ">?" + " AND " + END + ">=?"; @@ -2038,10 +1985,11 @@ public final class CalendarContract { public static final Uri CONTENT_URI_BY_INSTANCE = Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance"); - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; /** - * Helper for inserting an alarm time associated with an event + * Helper for inserting an alarm time associated with an event TODO move + * to Provider * * @hide */ @@ -2056,51 +2004,32 @@ public final class CalendarContract { values.put(CalendarAlerts.CREATION_TIME, currentTime); values.put(CalendarAlerts.RECEIVED_TIME, 0); values.put(CalendarAlerts.NOTIFY_TIME, 0); - values.put(CalendarAlerts.STATE, SCHEDULED); + values.put(CalendarAlerts.STATE, STATE_SCHEDULED); values.put(CalendarAlerts.MINUTES, minutes); return cr.insert(CONTENT_URI, values); } /** - * Queries alerts info using the given projection, selection filter, and - * ordering. This is a blocking call and should not be done on the UI - * thread. For selection and selectionArgs usage see - * {@link ContentResolver#query(Uri, String[], String, String[], String)} - * - * @param cr The content resolver to use for the query - * @param projection The columns to return - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param sortOrder How to order the rows as an SQL ORDER BY statement - * @return A Cursor containing the matching alerts - */ - public static final Cursor query(ContentResolver cr, String[] projection, - String selection, String[] selectionArgs, String sortOrder) { - return cr.query(CONTENT_URI, projection, selection, selectionArgs, - sortOrder); - } - - /** * Finds the next alarm after (or equal to) the given time and returns * the time of that alarm or -1 if no such alarm exists. This is a - * blocking call and should not be done on the UI thread. + * blocking call and should not be done on the UI thread. TODO move to + * provider * * @param cr the ContentResolver * @param millis the time in UTC milliseconds * @return the next alarm time greater than or equal to "millis", or -1 * if no such alarm exists. + * @hide */ public static final long findNextAlarmTime(ContentResolver cr, long millis) { String selection = ALARM_TIME + ">=" + millis; // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. String[] projection = new String[] { ALARM_TIME }; - Cursor cursor = query(cr, projection, - WHERE_FINDNEXTALARMTIME, - new String[] { + Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_FINDNEXTALARMTIME, + (new String[] { Long.toString(millis) - }, - SORT_ORDER_ALARMTIME_ASC); + }), SORT_ORDER_ALARMTIME_ASC); long alarmTime = -1; try { if (cursor != null && cursor.moveToFirst()) { @@ -2116,13 +2045,14 @@ public final class CalendarContract { /** * Searches the CalendarAlerts table for alarms that should have fired - * but have not and then reschedules them. This method can be called - * at boot time to restore alarms that may have been lost due to a - * phone reboot. + * but have not and then reschedules them. This method can be called at + * boot time to restore alarms that may have been lost due to a phone + * reboot. TODO move to provider * * @param cr the ContentResolver * @param context the Context * @param manager the AlarmManager + * @hide */ public static final void rescheduleMissedAlarms(ContentResolver cr, Context context, AlarmManager manager) { @@ -2136,15 +2066,10 @@ public final class CalendarContract { // TODO: construct an explicit SQL query so that we can add // "GROUPBY" instead of doing a sort and de-dup - Cursor cursor = CalendarAlerts.query(cr, - projection, - WHERE_RESCHEDULE_MISSED_ALARMS, - new String[] { - Long.toString(now), - Long.toString(ancient), - Long.toString(now) - }, - SORT_ORDER_ALARMTIME_ASC); + Cursor cursor = cr.query(CalendarAlerts.CONTENT_URI, projection, + WHERE_RESCHEDULE_MISSED_ALARMS, (new String[] { + Long.toString(now), Long.toString(ancient), Long.toString(now) + }), SORT_ORDER_ALARMTIME_ASC); if (cursor == null) { return; } @@ -2177,12 +2102,13 @@ public final class CalendarContract { * keep scheduled reminders up to date but apps may use this to * implement snooze functionality without modifying the reminders table. * Scheduled alarms will generate an intent using - * {@link #ACTION_EVENT_REMINDER}. + * {@link #ACTION_EVENT_REMINDER}. TODO Move to provider * * @param context A context for referencing system resources * @param manager The AlarmManager to use or null * @param alarmTime The time to fire the intent in UTC millis since * epoch + * @hide */ public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) { if (DEBUG) { @@ -2204,31 +2130,28 @@ public final class CalendarContract { } /** - * Searches for an entry in the CalendarAlerts table that matches - * the given event id, begin time and alarm time. If one is found - * then this alarm already exists and this method returns true. - * + * Searches for an entry in the CalendarAlerts table that matches the + * given event id, begin time and alarm time. If one is found then this + * alarm already exists and this method returns true. TODO Move to + * provider + * * @param cr the ContentResolver * @param eventId the event id to match * @param begin the start time of the event in UTC millis * @param alarmTime the alarm time of the event in UTC millis - * @return true if there is already an alarm for the given event - * with the same start time and alarm time. + * @return true if there is already an alarm for the given event with + * the same start time and alarm time. + * @hide */ public static final boolean alarmExists(ContentResolver cr, long eventId, long begin, long alarmTime) { // TODO: construct an explicit SQL query so that we can add // "LIMIT 1" to the end and get just one result. String[] projection = new String[] { ALARM_TIME }; - Cursor cursor = query(cr, - projection, - WHERE_ALARM_EXISTS, - new String[] { - Long.toString(eventId), - Long.toString(begin), - Long.toString(alarmTime) - }, - null); + Cursor cursor = cr.query(CONTENT_URI, projection, WHERE_ALARM_EXISTS, + (new String[] { + Long.toString(eventId), Long.toString(begin), Long.toString(alarmTime) + }), null); boolean found = false; try { if (cursor != null && cursor.getCount() > 0) { diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 4107c5a791d6..aae9ccffc309 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -266,7 +266,7 @@ public abstract class Layout { } } - Alignment align = mAlignment; + Alignment paraAlign = mAlignment; TabStops tabStops = null; boolean tabStopsIsInitialized = false; @@ -310,10 +310,10 @@ public abstract class Layout { ParagraphStyle.class); spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); - align = mAlignment; + paraAlign = mAlignment; for (int n = spans.length-1; n >= 0; n--) { if (spans[n] instanceof AlignmentSpan) { - align = ((AlignmentSpan) spans[n]).getAlignment(); + paraAlign = ((AlignmentSpan) spans[n]).getAlignment(); break; } } @@ -360,6 +360,16 @@ public abstract class Layout { tabStopsIsInitialized = true; } + // Determine whether the line aligns to normal, opposite, or center. + Alignment align = paraAlign; + if (align == Alignment.ALIGN_LEFT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? + Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; + } else if (align == Alignment.ALIGN_RIGHT) { + align = (dir == DIR_LEFT_TO_RIGHT) ? + Alignment.ALIGN_OPPOSITE : Alignment.ALIGN_NORMAL; + } + int x; if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { @@ -411,7 +421,9 @@ public abstract class Layout { int dir = getParagraphDirection(line); int x; - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + x = left; + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { x = left; } else { @@ -430,7 +442,9 @@ public abstract class Layout { } } int max = (int)getLineExtent(line, tabStops, false); - if (align == Alignment.ALIGN_OPPOSITE) { + if (align == Alignment.ALIGN_RIGHT) { + x = right - max; + } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { x = right - max; } else { @@ -738,11 +752,15 @@ public abstract class Layout { int dir = getParagraphDirection(line); Alignment align = getParagraphAlignment(line); - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + return 0; + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_RIGHT_TO_LEFT) return getParagraphRight(line) - getLineMax(line); else return 0; + } else if (align == Alignment.ALIGN_RIGHT) { + return mWidth - getLineMax(line); } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_RIGHT_TO_LEFT) return 0; @@ -765,11 +783,15 @@ public abstract class Layout { int dir = getParagraphDirection(line); Alignment align = getParagraphAlignment(line); - if (align == Alignment.ALIGN_NORMAL) { + if (align == Alignment.ALIGN_LEFT) { + return getParagraphLeft(line) + getLineMax(line); + } else if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_RIGHT_TO_LEFT) return mWidth; else return getParagraphLeft(line) + getLineMax(line); + } else if (align == Alignment.ALIGN_RIGHT) { + return mWidth; } else if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_RIGHT_TO_LEFT) return getLineMax(line); @@ -1765,8 +1787,10 @@ public abstract class Layout { ALIGN_NORMAL, ALIGN_OPPOSITE, ALIGN_CENTER, - // XXX ALIGN_LEFT, - // XXX ALIGN_RIGHT, + /** @hide */ + ALIGN_LEFT, + /** @hide */ + ALIGN_RIGHT, } private static final int TAB_INCREMENT = 20; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bf7f359303ef..bb5c95497989 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -9121,9 +9121,15 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit } /** - * Reset the resolved layout direction by clearing the corresponding flag + * Reset the resolved layout direction. + * + * Subclasses need to override this method to clear cached information that depends on the + * resolved layout direction, or to inform child views that inherit their layout direction. + * Overrides must also call the superclass implementation at the start of their implementation. + * + * @hide */ - void resetLayoutDirectionResolution() { + protected void resetLayoutDirectionResolution() { // Reset the current View resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED; } diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java index 2b692f3e93f1..d70c79850a18 100644 --- a/core/java/android/view/ViewAncestor.java +++ b/core/java/android/view/ViewAncestor.java @@ -4637,13 +4637,18 @@ public final class ViewAncestor extends Handler implements ViewParent, public void run() { if (mView != null) { - // Send the event directly since we do not want to append the - // source text because this is the text for the entire window - // and we just want to notify that the content has changed. - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - mView.onInitializeAccessibilityEvent(event); - AccessibilityManager.getInstance(mView.mContext).sendAccessibilityEvent(event); + // Check again for accessibility state since this is executed delayed. + AccessibilityManager accessibilityManager = + AccessibilityManager.getInstance(mView.mContext); + if (accessibilityManager.isEnabled()) { + // Send the event directly since we do not want to append the + // source text because this is the text for the entire window + // and we just want to notify that the content has changed. + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + mView.onInitializeAccessibilityEvent(event); + accessibilityManager.sendAccessibilityEvent(event); + } mIsPending = false; } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index f3a5050001ea..dbcbd6e5ae1f 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -19,6 +19,8 @@ package android.view; import android.app.AppGlobals; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.RemoteException; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.SparseArray; @@ -219,6 +221,9 @@ public class ViewConfiguration { private final int mOverscrollDistance; private final int mOverflingDistance; + private boolean sHasPermanentMenuKey; + private boolean sHasPermanentMenuKeySet; + private static final SparseArray<ViewConfiguration> sConfigurations = new SparseArray<ViewConfiguration>(2); @@ -254,11 +259,12 @@ public class ViewConfiguration { * @see android.util.DisplayMetrics */ private ViewConfiguration(Context context) { - final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final Resources res = context.getResources(); + final DisplayMetrics metrics = res.getDisplayMetrics(); + final Configuration config = res.getConfiguration(); final float density = metrics.density; final float sizeAndDensity; - if (context.getResources().getConfiguration().isLayoutSizeAtLeast( - Configuration.SCREENLAYOUT_SIZE_XLARGE)) { + if (config.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE)) { sizeAndDensity = density * 1.5f; } else { sizeAndDensity = density; @@ -280,6 +286,17 @@ public class ViewConfiguration { mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f); mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f); + + if (!sHasPermanentMenuKeySet) { + IWindowManager wm = Display.getWindowManager(); + try { + sHasPermanentMenuKey = wm.canStatusBarHide() && !res.getBoolean( + com.android.internal.R.bool.config_showNavigationBar); + sHasPermanentMenuKeySet = true; + } catch (RemoteException ex) { + sHasPermanentMenuKey = false; + } + } } /** @@ -640,4 +657,20 @@ public class ViewConfiguration { public static float getScrollFriction() { return SCROLL_FRICTION; } + + /** + * Report if the device has a permanent menu key available to the user. + * + * <p>As of Android 3.0, devices may not have a permanent menu key available. + * Apps should use the action bar to present menu options to users. + * However, there are some apps where the action bar is inappropriate + * or undesirable. This method may be used to detect if a menu key is present. + * If not, applications should provide another on-screen affordance to access + * functionality. + * + * @return true if a permanent menu key is present, false otherwise. + */ + public boolean hasPermanentMenuKey() { + return sHasPermanentMenuKey; + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 2a90ddea3b55..da88fbb8fff3 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1746,6 +1746,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final long now = SystemClock.uptimeMillis(); event = MotionEvent.obtain(now, now, MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); syntheticEvent = true; } @@ -4998,12 +4999,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager viewAncestor.requestTransitionStart(transition); } - /** - * This method will be called when we need to reset the layout direction resolution flag - * - */ @Override - void resetLayoutDirectionResolution() { + protected void resetLayoutDirectionResolution() { super.resetLayoutDirectionResolution(); // Take care of resetting the children resolution too diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 25f01a7dc9fa..ac867690724b 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -25,40 +25,51 @@ import java.util.ArrayList; import java.util.List; /** + * <p> * This class represents accessibility events that are sent by the system when * something notable happens in the user interface. For example, when a * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. + * </p> * <p> * An accessibility event is fired by an individual view which populates the event with - * a record for its state and requests from its parent to send the event to interested - * parties. The parent can optionally add a record for itself before dispatching a similar - * request to its parent. A parent can also choose not to respect the request for sending - * an event. The accessibility event is sent by the topmost view in the view tree. - * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore - * all records in an accessibility event to obtain more information about the context - * in which the event was fired. + * data for its state and requests from its parent to send the event to interested + * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before + * dispatching a similar request to its parent. A parent can also choose not to respect the + * request for sending an event. The accessibility event is sent by the topmost view in the + * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can + * explore all records in an accessibility event to obtain more information about the + * context in which the event was fired. + * </p> * <p> - * A client can add, remove, and modify records. The getters and setters for individual - * properties operate on the current record which can be explicitly set by the client. By - * default current is the first record. Thus, querying a record would require setting - * it as the current one and interacting with the property getters and setters. + * The main purpose of an accessibility event is to expose enough information for an + * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback + * to the user. Sometimes however, an accessibility service may need more contextual + * information then the one in the event pay-load. In such cases the service can obtain + * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state) + * which can be used for exploring the window content. Note that the privilege for accessing + * an event's source, thus the window content, has to be explicitly requested. For more + * details refer to {@link android.accessibilityservice.AccessibilityService}. If an + * accessibility service has not requested to retrieve the window content the event will + * not contain reference to its source. Also for events of type + * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available. + * </p> * <p> * This class represents various semantically different accessibility event - * types. Each event type has associated a set of related properties. In other + * types. Each event type has an associated set of related properties. In other * words, each event type is characterized via a subset of the properties exposed * by this class. For each event type there is a corresponding constant defined - * in this class. Since some event types are semantically close there are mask - * constants that group them together. Follows a specification of the event - * types and their associated properties: + * in this class. Follows a specification of the event types and their associated properties: + * </p> * <p> - * <b>VIEW TYPES</b> <br> + * <b>VIEW TYPES</b></br> + * </p> * <p> * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View} - * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> - * Type:{@link #TYPE_VIEW_CLICKED} <br> - * Properties:</br> + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.</br> + * <em>Type:</em>{@link #TYPE_VIEW_CLICKED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -67,13 +78,14 @@ import java.util.List; * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * </ul> + * </p> * <p> * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View} - * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> - * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br> - * Properties:</br> + * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc </br> + * <em>Type:</em>{@link #TYPE_VIEW_LONG_CLICKED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -82,13 +94,14 @@ import java.util.List; * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> * </ul> + * </p> * <p> * <b>View selected</b> - represents the event of selecting an item usually in - * the context of an {@link android.widget.AdapterView}. <br> - * Type: {@link #TYPE_VIEW_SELECTED} <br> - * Properties:</br> + * the context of an {@link android.widget.AdapterView}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_SELECTED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -96,17 +109,17 @@ import java.util.List; * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} -The number of selectable items of the source.</li> + * <li>{@link #getItemCount()} - The number of selectable items of the source.</li> * <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li> * </ul> - * <p> + * </p> * <p> * <b>View focused</b> - represents the event of focusing a - * {@link android.view.View}. <br> - * Type: {@link #TYPE_VIEW_FOCUSED} <br> - * Properties:</br> + * {@link android.view.View}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_FOCUSED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -114,16 +127,17 @@ import java.util.List; * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #isPassword()} - Whether the source is password.</li> * <li>{@link #isChecked()} - Whether the source is checked.</li> - * <li>{@link #getItemCount()} -The number of focusable items on the screen.</li> + * <li>{@link #getItemCount()} - The number of focusable items on the screen.</li> * <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li> * </ul> + * </p> * <p> * <b>View text changed</b> - represents the event of changing the text of an - * {@link android.widget.EditText}. <br> - * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br> - * Properties:</br> + * {@link android.widget.EditText}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -136,13 +150,14 @@ import java.util.List; * <li>{@link #getRemovedCount()} - The number of removed characters.</li> * <li>{@link #getBeforeText()} - The text of the source before the change.</li> * </ul> + * </p> * <p> * <b>View text selection changed</b> - represents the event of changing the text - * selection of an {@link android.widget.EditText}.<br> - * Type: {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} <br> - * Properties:</br> + * selection of an {@link android.widget.EditText}.</br> + * <em>Type:</em> {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} </br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -152,7 +167,8 @@ import java.util.List; * <li>{@link #getFromIndex()} - The selection start index.</li> * <li>{@link #getToIndex()} - The selection end index.</li> * <li>{@link #getItemCount()} - The length of the source text.</li> - * <ul> + * </ul> + * </p> * <p> * <b>View scrolled</b> - represents the event of scrolling a view. If * the source is a descendant of {@link android.widget.AdapterView} the @@ -161,11 +177,11 @@ import java.util.List; * is unaware if its pixel size since its adapter is responsible for * creating views. In all other cases the scroll is reported as the current * scroll on the X and Y axis respectively plus the height of the source in - * pixels.<br> - * Type: {@link #TYPE_VIEW_SCROLLED} <br> - * Properties:</br> + * pixels.</br> + * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> @@ -181,41 +197,49 @@ import java.util.List; * (for descendants of AdapterView).</li> * <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView) * or the height of the source in pixels (all other cases).</li> - * <ul> - * <p> - * <b>TRANSITION TYPES</b> <br> + * </ul> + * </p> * <p> + * <b>TRANSITION TYPES</b></br> + * </p> * <b>Window state changed</b> - represents the event of opening a * {@link android.widget.PopupWindow}, {@link android.view.Menu}, - * {@link android.app.Dialog}, etc. <br> - * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br> - * Properties:</br> + * {@link android.app.Dialog}, etc.</br> + * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getText()} - The text of the source.</li> * </ul> + * </p> * <p> * <b>Window content changed</b> - represents the event of change in the * content of a window. This change can be adding/removing view, changing - * a view size, etc.<br> - * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br> - * Properties:</br> + * a view size, etc.</br> + * <p> + * <strong>Note:</strong> This event is fired only for the window source of the + * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED}) + * and its purpose is to notify clients that the content of the user interaction + * window has changed. + * </p> + * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br> + * <em>Properties:</em></br> * <ul> - * <li>{@link #getSource()} - The source info (for registered clients).</li> * + * <li>{@link #getSource()} - The source info (for registered clients).</li> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> * <li>{@link #getEventTime()} - The event time.</li> - * <ul> + * </ul> * <p> - * <b>NOTIFICATION TYPES</b> <br> + * <b>NOTIFICATION TYPES</b></br> * <p> - * <b>Notification state changed</b> - represents the event showing/hiding + * <b>Notification state changed</b> - represents the event showing * {@link android.app.Notification}. - * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br> - * Properties:</br> + * <em>Type:</em> {@link #TYPE_NOTIFICATION_STATE_CHANGED}</br> + * <em>Properties:</em></br> * <ul> * <li>{@link #getClassName()} - The class name of the source.</li> * <li>{@link #getPackageName()} - The package name of the source.</li> @@ -223,15 +247,17 @@ import java.util.List; * <li>{@link #getText()} - The text of the source.</li> * <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li> * </ul> + * </p> * <p> * <b>Security note</b> * <p> - * Since an event contains the text of its source privacy can be compromised by leaking of + * Since an event contains the text of its source privacy can be compromised by leaking * sensitive information such as passwords. To address this issue any event fired in response * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. * * @see android.view.accessibility.AccessibilityManager * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityNodeInfo */ public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { private static final boolean DEBUG = false; @@ -285,13 +311,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; /** - * Represents the event of opening/closing a {@link android.widget.PopupWindow}, + * Represents the event of opening a {@link android.widget.PopupWindow}, * {@link android.view.Menu}, {@link android.app.Dialog}, etc. */ public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; /** - * Represents the event showing/hiding a {@link android.app.Notification}. + * Represents the event showing a {@link android.app.Notification}. */ public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; @@ -340,6 +366,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * @see #TYPE_VIEW_TEXT_CHANGED * @see #TYPE_WINDOW_STATE_CHANGED * @see #TYPE_NOTIFICATION_STATE_CHANGED + * @see #TYPE_VIEW_HOVER_ENTER + * @see #TYPE_VIEW_HOVER_EXIT + * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START + * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END + * @see #TYPE_WINDOW_CONTENT_CHANGED + * @see #TYPE_VIEW_SCROLLED + * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED */ public static final int TYPES_ALL_MASK = 0xFFFFFFFF; @@ -432,10 +465,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Gets the records at a given index. + * Gets the record at a given index. * * @param index The index. - * @return The records at the specified index. + * @return The record at the specified index. */ public AccessibilityRecord getRecord(int index) { return mRecords.get(index); @@ -506,7 +539,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is - * instantiated with type property set. + * instantiated with its type property set. * * @param eventType The event type. * @return An instance. @@ -519,7 +552,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Returns a cached instance if such is available or a new one is - * instantiated with type property set. + * initialized with from the given <code>event</code>. * * @param event The other event. * @return An instance. @@ -559,9 +592,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par } /** - * Return an instance back to be reused. + * Recycles an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <b>Note: You must not touch the object after calling this function.</b> + * </p> * * @throws IllegalStateException If the event is already recycled. */ @@ -714,7 +748,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("; EventType: ").append(eventTypeToString(mEventType)); + builder.append("EventType: ").append(eventTypeToString(mEventType)); builder.append("; EventTime: ").append(mEventTime); builder.append("; PackageName: ").append(mPackageName); builder.append(super.toString()); @@ -758,11 +792,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par * Returns the string representation of an event type. For example, * {@link #TYPE_VIEW_CLICKED} is represented by the string TYPE_VIEW_CLICKED. * - * @param feedbackType The event type + * @param eventType The event type * @return The string representation. */ - public static String eventTypeToString(int feedbackType) { - switch (feedbackType) { + public static String eventTypeToString(int eventType) { + switch (eventType) { case TYPE_VIEW_CLICKED: return "TYPE_VIEW_CLICKED"; case TYPE_VIEW_LONG_CLICKED: diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java index 3d70959b1ef9..f11880bc8c07 100644 --- a/core/java/android/view/accessibility/AccessibilityEventSource.java +++ b/core/java/android/view/accessibility/AccessibilityEventSource.java @@ -24,11 +24,12 @@ public interface AccessibilityEventSource { /** * Handles the request for sending an {@link AccessibilityEvent} given * the event type. The method must first check if accessibility is on - * via calling {@link AccessibilityManager#isEnabled()}, obtain - * an {@link AccessibilityEvent} from the event pool through calling - * {@link AccessibilityEvent#obtain(int)}, populate the event, and - * send it for dispatch via calling - * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}. + * via calling {@link AccessibilityManager#isEnabled() AccessibilityManager.isEnabled()}, + * obtain an {@link AccessibilityEvent} from the event pool through calling + * {@link AccessibilityEvent#obtain(int) AccessibilityEvent.obtain(int)}, populate the + * event, and send it for dispatch via calling + * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent) + * AccessibilityManager.sendAccessibilityEvent(AccessibilityEvent)}. * * @see AccessibilityEvent * @see AccessibilityManager @@ -41,7 +42,8 @@ public interface AccessibilityEventSource { * Handles the request for sending an {@link AccessibilityEvent}. The * method does not guarantee to check if accessibility is on before * sending the event for dispatch. It is responsibility of the caller - * to do the check via calling {@link AccessibilityManager#isEnabled()}. + * to do the check via calling {@link AccessibilityManager#isEnabled() + * AccessibilityManager.isEnabled()}. * * @see AccessibilityEvent * @see AccessibilityManager diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index eece64af3e16..314b7ca35821 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -37,16 +37,30 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** - * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. - * Such events are generated when something notable happens in the user interface, + * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, + * and provides facilities for querying the accessibility state of the system. + * Accessibility events are generated when something notable happens in the user interface, * for example an {@link android.app.Activity} starts, the focus or selection of a * {@link android.view.View} changes etc. Parties interested in handling accessibility * events implement and register an accessibility service which extends * {@link android.accessibilityservice.AccessibilityService}. + * <p> + * To obtain a handle to the accessibility manager do the following: + * </p> + * <p> + * <code> + * <pre> + * AccessibilityManager accessibilityManager = + * (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); + * </pre> + * </code> + * </p> * * @see AccessibilityEvent + * @see AccessibilityNodeInfo * @see android.accessibilityservice.AccessibilityService - * @see android.content.Context#getSystemService + * @see Context#getSystemService + * @see Context#ACCESSIBILITY_SERVICE */ public final class AccessibilityManager { private static final boolean DEBUG = false; @@ -72,10 +86,11 @@ public final class AccessibilityManager { * Listener for the accessibility state. */ public interface AccessibilityStateChangeListener { + /** * Called back on change in the accessibility state. * - * @param enabled + * @param enabled Whether accessibility is enabled. */ public void onAccessibilityStateChanged(boolean enabled); } @@ -142,9 +157,9 @@ public final class AccessibilityManager { } /** - * Returns if the {@link AccessibilityManager} is enabled. + * Returns if the accessibility in the system is enabled. * - * @return True if this {@link AccessibilityManager} is enabled, false otherwise. + * @return True if accessibility is enabled, false otherwise. */ public boolean isEnabled() { synchronized (mHandler) { @@ -161,17 +176,15 @@ public final class AccessibilityManager { * @hide */ public IAccessibilityManagerClient getClient() { - return (IAccessibilityManagerClient) mClient.asBinder(); + return (IAccessibilityManagerClient) mClient.asBinder(); } /** - * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not - * enabled the call is a NOOP. + * Sends an {@link AccessibilityEvent}. * - * @param event The {@link AccessibilityEvent}. + * @param event The event to send. * - * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} - * while accessibility is not enabled. + * @throws IllegalStateException if accessibility is not enabled. */ public void sendAccessibilityEvent(AccessibilityEvent event) { if (!mIsEnabled) { @@ -199,7 +212,7 @@ public final class AccessibilityManager { } /** - * Requests interruption of the accessibility feedback from all accessibility services. + * Requests feedback interruption from all accessibility services. */ public void interrupt() { if (!mIsEnabled) { @@ -256,13 +269,20 @@ public final class AccessibilityManager { * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services * for a given feedback type. * - * @param feedbackType The feedback type (can be bitwise or of multiple types). + * @param feedbackTypeFlags The feedback type flags. * @return An unmodifiable list with {@link AccessibilityServiceInfo}s. + * + * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE + * @see AccessibilityServiceInfo#FEEDBACK_GENERIC + * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC + * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN + * @see AccessibilityServiceInfo#FEEDBACK_VISUAL */ - public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) { + public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList( + int feedbackTypeFlags) { List<AccessibilityServiceInfo> services = null; try { - services = mService.getEnabledAccessibilityServiceList(feedbackType); + services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags); if (DEBUG) { Log.i(LOG_TAG, "Installed AccessibilityServices " + services); } @@ -273,7 +293,8 @@ public final class AccessibilityManager { } /** - * Registers an {@link AccessibilityStateChangeListener}. + * Registers an {@link AccessibilityStateChangeListener} for changes in + * the global accessibility state of the system. * * @param listener The listener. * @return True if successfully registered. diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index dbbe7be4d533..031c6aeffd21 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -22,7 +22,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.text.TextUtils; -import android.util.SparseArray; import android.util.SparseIntArray; import android.view.View; @@ -30,12 +29,26 @@ import java.util.Collections; import java.util.List; /** - * This class represents a node of the screen content. From the point of - * view of an accessibility service the screen content is presented as tree - * of accessibility nodes. + * This class represents a node of the window content as well as actions that + * can be requested from its source. From the point of view of an + * {@link android.accessibilityservice.AccessibilityService} a window content is + * presented as tree of accessibility node info which may or may not map one-to-one + * to the view hierarchy. In other words, a custom view is free to report itself as + * a tree of accessibility node info. + * </p> + * <p> + * Once an accessibility node info is delivered to an accessibility service it is + * made immutable and calling a state mutation method generates an error. + * </p> + * <p> + * Please refer to {@link android.accessibilityservice.AccessibilityService} for + * details about how to obtain a handle to window content as a tree of accessibility + * node info as well as familiarizing with the security model. + * </p> * - * TODO(svertoslavganov): Update the documentation, add sample, and describe - * the security policy. + * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityEvent + * @see AccessibilityManager */ public class AccessibilityNodeInfo implements Parcelable { @@ -85,9 +98,6 @@ public class AccessibilityNodeInfo implements Parcelable { private static final int PROPERTY_SCROLLABLE = 0x00000200; - // Readable representations - lazily initialized. - private static SparseArray<String> sActionSymbolicNames; - // Housekeeping. private static final int MAX_POOL_SIZE = 50; private static final Object sPoolLock = new Object(); @@ -154,12 +164,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Get the child at given index. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. * </p> + * * @param index The child index. * @return The child node. * @@ -184,9 +193,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Adds a child. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param child The child. * * @throws IllegalStateException If called from an AccessibilityService. @@ -215,9 +226,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Adds an action that can be performed on the node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param action The action. * * @throws IllegalStateException If called from an AccessibilityService. @@ -230,9 +243,10 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Performs an action on the node. * <p> - * Note: An action can be performed only if the request is made + * <strong>Note:</strong> An action can be performed only if the request is made * from an {@link android.accessibilityservice.AccessibilityService}. * </p> + * * @param action The action to perform. * @return True if the action was performed. * @@ -256,6 +270,11 @@ public class AccessibilityNodeInfo implements Parcelable { * Finds {@link AccessibilityNodeInfo}s by text. The match is case * insensitive containment. The search is relative to this info i.e. * this info is the root of the traversed tree. + * <p> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + * </p> * * @param text The searched text. * @return A list of node info. @@ -277,12 +296,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Gets the unique id identifying this node's parent. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the + * received info by calling {@link AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. * </p> + * * @return The node's patent id. */ public AccessibilityNodeInfo getParent() { @@ -302,9 +320,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the parent. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param parent The parent. * * @throws IllegalStateException If called from an AccessibilityService. @@ -327,9 +347,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the node bounds in parent coordinates. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. @@ -352,9 +374,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the node bounds in screen coordinates. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param bounds The node bounds. * * @throws IllegalStateException If called from an AccessibilityService. @@ -376,9 +400,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is checkable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param checkable True if the node is checkable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -399,9 +425,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is checked. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param checked True if the node is checked. * * @throws IllegalStateException If called from an AccessibilityService. @@ -422,9 +450,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is focusable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param focusable True if the node is focusable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -445,9 +475,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is focused. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param focused True if the node is focused. * * @throws IllegalStateException If called from an AccessibilityService. @@ -468,9 +500,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is selected. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param selected True if the node is selected. * * @throws IllegalStateException If called from an AccessibilityService. @@ -491,9 +525,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is clickable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param clickable True if the node is clickable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -514,9 +550,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is long clickable. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param longClickable True if the node is long clickable. * * @throws IllegalStateException If called from an AccessibilityService. @@ -537,9 +575,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is enabled. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param enabled True if the node is enabled. * * @throws IllegalStateException If called from an AccessibilityService. @@ -560,9 +600,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets whether this node is a password. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param password True if the node is a password. * * @throws IllegalStateException If called from an AccessibilityService. @@ -582,6 +624,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets if the node is scrollable. + * <p> + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. + * This class is made immutable before being delivered to an AccessibilityService. + * </p> * * @param scrollable True if the node is scrollable, false otherwise. * @@ -604,9 +651,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the package this node comes from. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param packageName The package name. * * @throws IllegalStateException If called from an AccessibilityService. @@ -628,9 +677,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the class this node comes from. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param className The class name. * * @throws IllegalStateException If called from an AccessibilityService. @@ -652,9 +703,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the text of this node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param text The text. * * @throws IllegalStateException If called from an AccessibilityService. @@ -676,9 +729,11 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Sets the content description of this node. * <p> - * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. + * <strong>Note:</strong> Cannot be called from an + * {@link android.accessibilityservice.AccessibilityService}. * This class is made immutable before being delivered to an AccessibilityService. * </p> + * * @param contentDescription The content description. * * @throws IllegalStateException If called from an AccessibilityService. @@ -820,7 +875,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Return an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <strong>Note:</strong> You must not touch the object after calling this function. * * @throws IllegalStateException If the info is already recycled. */ @@ -842,8 +897,8 @@ public class AccessibilityNodeInfo implements Parcelable { /** * {@inheritDoc} * <p> - * <b>Note: After the instance is written to a parcel it is recycled. - * You must not touch the object after calling this function.</b> + * <strong>Note:</strong> After the instance is written to a parcel it + * is recycled. You must not touch the object after calling this function. * </p> */ public void writeToParcel(Parcel parcel, int flags) { @@ -885,7 +940,7 @@ public class AccessibilityNodeInfo implements Parcelable { TextUtils.writeToParcel(mContentDescription, parcel, flags); // Since instances of this class are fetched via synchronous i.e. blocking - // calls in IPCs and we always recycle as soon as the instance is marshaled. + // calls in IPCs we always recycle as soon as the instance is marshaled. recycle(); } @@ -957,15 +1012,18 @@ public class AccessibilityNodeInfo implements Parcelable { * @return The symbolic name. */ private static String getActionSymbolicName(int action) { - SparseArray<String> actionSymbolicNames = sActionSymbolicNames; - if (actionSymbolicNames == null) { - actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>(); - actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS"); - actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS"); - actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT"); - actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT"); + switch (action) { + case ACTION_FOCUS: + return "ACTION_FOCUS"; + case ACTION_CLEAR_FOCUS: + return "ACTION_CLEAR_FOCUS"; + case ACTION_SELECT: + return "ACTION_SELECT"; + case ACTION_CLEAR_SELECTION: + return "ACTION_CLEAR_SELECTION"; + default: + throw new IllegalArgumentException("Unknown action: " + action); } - return actionSymbolicNames.get(action); } private boolean canPerformRequestOverConnection(int accessibilityViewId) { diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java index b9815c5bbaef..f4d5e897e422 100644 --- a/core/java/android/view/accessibility/AccessibilityRecord.java +++ b/core/java/android/view/accessibility/AccessibilityRecord.java @@ -25,12 +25,28 @@ import java.util.ArrayList; import java.util.List; /** - * Represents a record in an accessibility event. This class encapsulates - * the information for a {@link android.view.View}. Note that not all properties - * are applicable to all view types. For detailed information please refer to - * {@link AccessibilityEvent}. + * Represents a record in an {@link AccessibilityEvent} and contains information + * about state change of its source {@link android.view.View}. When a view fires + * an accessibility event it requests from its parent to dispatch the + * constructed event. The parent may optionally append a record for itself + * for providing more context to + * {@link android.accessibilityservice.AccessibilityService}s. Hence, + * accessibility services can facilitate additional accessibility records + * to enhance feedback. + * </p> + * <p> + * Once the accessibility event containing a record is dispatched the record is + * made immutable and calling a state mutation method generates an error. + * </p> + * <p> + * <strong>Note:</strong> Not all properties are applicable to all accessibility + * event types. For detailed information please refer to {@link AccessibilityEvent}. + * </p> * * @see AccessibilityEvent + * @see AccessibilityManager + * @see android.accessibilityservice.AccessibilityService + * @see AccessibilityNodeInfo */ public class AccessibilityRecord { @@ -79,32 +95,6 @@ public class AccessibilityRecord { } /** - * Initialize this record from another one. - * - * @param record The to initialize from. - */ - void init(AccessibilityRecord record) { - mSealed = record.mSealed; - mBooleanProperties = record.mBooleanProperties; - mCurrentItemIndex = record.mCurrentItemIndex; - mItemCount = record.mItemCount; - mFromIndex = record.mFromIndex; - mToIndex = record.mToIndex; - mScrollX = record.mScrollX; - mScrollY = record.mScrollY; - mAddedCount = record.mAddedCount; - mRemovedCount = record.mRemovedCount; - mClassName = record.mClassName; - mContentDescription = record.mContentDescription; - mBeforeText = record.mBeforeText; - mParcelableData = record.mParcelableData; - mText.addAll(record.mText); - mSourceWindowId = record.mSourceWindowId; - mSourceViewId = record.mSourceViewId; - mConnection = record.mConnection; - } - - /** * Sets the event source. * * @param source The source. @@ -125,13 +115,12 @@ public class AccessibilityRecord { /** * Gets the {@link AccessibilityNodeInfo} of the event source. * <p> - * <strong> - * It is a client responsibility to recycle the received info by - * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating - * of multiple instances. - * </strong> + * <strong>Note:</strong> It is a client responsibility to recycle the received info + * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()} + * to avoid creating of multiple instances. + * * </p> - * @return The info. + * @return The info of the source. */ public AccessibilityNodeInfo getSource() { enforceSealed(); @@ -641,7 +630,7 @@ public class AccessibilityRecord { /** * Return an instance back to be reused. * <p> - * <b>Note: You must not touch the object after calling this function.</b> + * <strong>Note:</strong> You must not touch the object after calling this function. * * @throws IllegalStateException If the record is already recycled. */ @@ -661,6 +650,32 @@ public class AccessibilityRecord { } /** + * Initialize this record from another one. + * + * @param record The to initialize from. + */ + void init(AccessibilityRecord record) { + mSealed = record.mSealed; + mBooleanProperties = record.mBooleanProperties; + mCurrentItemIndex = record.mCurrentItemIndex; + mItemCount = record.mItemCount; + mFromIndex = record.mFromIndex; + mToIndex = record.mToIndex; + mScrollX = record.mScrollX; + mScrollY = record.mScrollY; + mAddedCount = record.mAddedCount; + mRemovedCount = record.mRemovedCount; + mClassName = record.mClassName; + mContentDescription = record.mContentDescription; + mBeforeText = record.mBeforeText; + mParcelableData = record.mParcelableData; + mText.addAll(record.mText); + mSourceWindowId = record.mSourceWindowId; + mSourceViewId = record.mSourceViewId; + mConnection = record.mConnection; + } + + /** * Clears the state of this instance. */ void clear() { diff --git a/core/java/android/view/accessibility/package.html b/core/java/android/view/accessibility/package.html new file mode 100644 index 000000000000..4afafd3ca128 --- /dev/null +++ b/core/java/android/view/accessibility/package.html @@ -0,0 +1,39 @@ +<html> +<body> +<p> + The classes in this package are used to represent screen content and changes to it + as well as APIs for querying the global accessibility state of the system. +</p> +<p> + {@link android.view.accessibility.AccessibilityEvent}s are sent by the system when + something notable happens in the user interface. For example, when a + {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. +</p> +<p> + {@link android.view.accessibility.AccessibilityRecord} contains information + about state change of its source {@link android.view.View}. When a view fires + an accessibility event it requests from its parent to dispatch the + constructed event. The parent may optionally append a record for itself for + providing more context to {@link android.accessibilityservice.AccessibilityService}s. + Hence, accessibility services can facilitate additional accessibility records + to enhance feedback. +</p> +<p> + {@link android.view.accessibility.AccessibilityNodeInfo} represents a node of the + window content as well as actions that can be requested from its source. From the point + of view of an {@link android.accessibilityservice.AccessibilityService} a window content is + presented as tree of accessibility node info which may or may not map one-to-one + to the view hierarchy. In other words, a custom view is free to report itself as + a tree of accessibility node info. +</p> +<p> + {@link android.view.accessibility.AccessibilityManager} is a system level service that + serves as an event dispatch for {@link android.view.accessibility.AccessibilityEvent}s, + and provides facilities for querying the accessibility state of the system. Accessibility + events are generated when something notable happens in the user interface, for example an + {@link android.app.Activity} starts, the focus or selection of a {@link android.view.View} + changes etc. Parties interested in handling accessibility events implement and register an + accessibility service which extends {@link android.accessibilityservice.AccessibilityService}. +</p> +</body> +</html> diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 8fae7ecde139..4f97066aa381 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -1950,6 +1950,7 @@ public final class WebViewCore { // mInitialViewState is set by didFirstLayout() and then reset in the // next webkitDraw after passing the state to the UI thread. private ViewState mInitialViewState = null; + private boolean mFirstLayoutForNonStandardLoad; static class ViewState { float mMinScale; @@ -1977,6 +1978,7 @@ public final class WebViewCore { int mMinPrefWidth; // only non-null if it is for the first picture set after the first layout ViewState mViewState; + boolean mFirstLayoutForNonStandardLoad; boolean mFocusSizeChanged; } @@ -2026,6 +2028,10 @@ public final class WebViewCore { draw.mViewState = mInitialViewState; mInitialViewState = null; } + if (mFirstLayoutForNonStandardLoad) { + draw.mFirstLayoutForNonStandardLoad = true; + mFirstLayoutForNonStandardLoad = false; + } if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID"); Message.obtain(mWebView.mPrivateHandler, WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget(); @@ -2312,6 +2318,8 @@ public final class WebViewCore { // if mViewportWidth is 0, it means device-width, always update. if (mViewportWidth != 0 && !updateViewState) { + // For non standard load, since updateViewState will be false. + mFirstLayoutForNonStandardLoad = true; ViewState viewState = new ViewState(); viewState.mMinScale = mViewportMinimumScale / 100.0f; viewState.mMaxScale = mViewportMaximumScale / 100.0f; diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 6c6974b468ec..7d43e94ff695 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -1024,6 +1024,11 @@ class ZoomManager { } else { mInZoomOverview = !scaleHasDiff; } + if (drawData.mFirstLayoutForNonStandardLoad && settings.getLoadWithOverviewMode()) { + // Set mInitialZoomOverview in case this is the first picture for non standard load, + // so next new picture could be forced into overview mode if it's true. + mInitialZoomOverview = mInZoomOverview; + } } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2a70ac87f2b8..939779fe36f4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -340,6 +340,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private WordIterator mWordIterator; + // The alignment to pass to Layout, or null if not resolved. + private Layout.Alignment mLayoutAlignment; + + // The default value for mTextAlign. + private TextAlign mTextAlign = TextAlign.INHERIT; + + private static enum TextAlign { + INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; + } + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -5532,6 +5542,73 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener physicalWidth, false); } + @Override + protected void resetLayoutDirectionResolution() { + super.resetLayoutDirectionResolution(); + + if (mLayoutAlignment != null && + (mTextAlign == TextAlign.VIEW_START || + mTextAlign == TextAlign.VIEW_END)) { + mLayoutAlignment = null; + } + } + + private Layout.Alignment getLayoutAlignment() { + if (mLayoutAlignment == null) { + Layout.Alignment alignment; + TextAlign textAlign = mTextAlign; + switch (textAlign) { + case INHERIT: + // fall through to gravity temporarily + // intention is to inherit value through view hierarchy. + case GRAVITY: + switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) { + case Gravity.START: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + case Gravity.END: + alignment = Layout.Alignment.ALIGN_OPPOSITE; + break; + case Gravity.LEFT: + alignment = Layout.Alignment.ALIGN_LEFT; + break; + case Gravity.RIGHT: + alignment = Layout.Alignment.ALIGN_RIGHT; + break; + case Gravity.CENTER_HORIZONTAL: + alignment = Layout.Alignment.ALIGN_CENTER; + break; + default: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + } + break; + case TEXT_START: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + case TEXT_END: + alignment = Layout.Alignment.ALIGN_OPPOSITE; + break; + case CENTER: + alignment = Layout.Alignment.ALIGN_CENTER; + break; + case VIEW_START: + alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? + Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT; + break; + case VIEW_END: + alignment = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ? + Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT; + break; + default: + alignment = Layout.Alignment.ALIGN_NORMAL; + break; + } + mLayoutAlignment = alignment; + } + return mLayoutAlignment; + } + /** * The width passed in is now the desired layout width, * not the full view width with padding. @@ -5552,25 +5629,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener hintWidth = 0; } - final int layoutDirection = getResolvedLayoutDirection(); - final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - - Layout.Alignment alignment; - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - alignment = Layout.Alignment.ALIGN_CENTER; - break; - - case Gravity.RIGHT: - // Note, Layout resolves ALIGN_OPPOSITE to left or - // right based on the paragraph direction. - alignment = Layout.Alignment.ALIGN_OPPOSITE; - break; - - default: - alignment = Layout.Alignment.ALIGN_NORMAL; - } - + Layout.Alignment alignment = getLayoutAlignment(); boolean shouldEllipsize = mEllipsize != null && mInput == null; if (mText instanceof Spannable) { diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 8d5df6f96714..519acf5d3eca 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,7 +16,6 @@ package com.android.internal.app; -import com.android.internal.R; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; @@ -36,7 +35,6 @@ import android.app.Dialog; import android.app.FragmentTransaction; import android.content.Context; import android.content.res.Configuration; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.ActionMode; @@ -580,6 +578,9 @@ public class ActionBarImpl extends ActionBar { mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); + if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) { + mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); + } } /** @@ -620,6 +621,7 @@ public class ActionBarImpl extends ActionBar { // Clear out the context mode views after the animation finishes mContextView.closeMode(); + mActionMode = null; if (mWasHiddenBeforeMode) { diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 322a8545d559..2fec9cd09f5b 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -26,6 +26,7 @@ import android.view.MenuItem; import android.view.SoundEffectConstants; import android.view.View; import android.view.View.MeasureSpec; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.ImageButton; @@ -69,9 +70,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { final Resources res = context.getResources(); if (!mReserveOverflowSet) { - // TODO Use the no-buttons specifier instead here - mReserveOverflow = res.getConfiguration() - .isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE); + mReserveOverflow = !ViewConfiguration.get(context).hasPermanentMenuKey(); } if (!mWidthLimitSet) { diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index d710cfae56f3..953328c03ddd 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -105,24 +105,19 @@ public class ActionBarContainer extends FrameLayout { public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int nonTabHeight = 0; - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); + if (mActionBarView == null) return; - if (child == mTabContainer) continue; - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - nonTabHeight = Math.max(nonTabHeight, - child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); - } + final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams(); + final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 : + mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { final int mode = MeasureSpec.getMode(heightMeasureSpec); if (mode == MeasureSpec.AT_MOST) { final int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(getMeasuredWidth(), - Math.min(nonTabHeight + mTabContainer.getMeasuredHeight(), maxHeight)); + Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(), + maxHeight)); } } } @@ -137,12 +132,14 @@ public class ActionBarContainer extends FrameLayout { if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) { // Not showing home, put tabs on top. final int count = getChildCount(); - for (int i = 0; i < count; i++){ + for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child == mTabContainer) continue; - child.offsetTopAndBottom(tabHeight); + if (!mActionBarView.isCollapsed()) { + child.offsetTopAndBottom(tabHeight); + } } mTabContainer.layout(l, 0, r, tabHeight); } else { diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index fc439944f518..3e3eeab4cbed 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -287,7 +287,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0); } - if (mMenuView != null) { + if (mMenuView != null && mMenuView.getParent() == this) { availableWidth = measureChildView(mMenuView, availableWidth, childSpecHeight, 0); } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 8eb046ebd94d..09bc1fc3cf7f 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -117,6 +117,7 @@ public class ActionBarView extends AbsActionBarView { private boolean mUserTitle; private boolean mIncludeTabs; private boolean mIsCollapsable; + private boolean mIsCollapsed; private MenuBuilder mOptionsMenu; @@ -692,6 +693,10 @@ public class ActionBarView extends AbsActionBarView { mIsCollapsable = collapsable; } + public boolean isCollapsed() { + return mIsCollapsed; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int childCount = getChildCount(); @@ -708,9 +713,11 @@ public class ActionBarView extends AbsActionBarView { if (visibleChildren == 0) { // No size for an empty action bar when collapsable. setMeasuredDimension(0, 0); + mIsCollapsed = true; return; } } + mIsCollapsed = false; int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 5b4d7ab143fa..2f7adf0c0b46 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -15,6 +15,9 @@ */ package com.android.internal.widget; +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; import android.app.ActionBar; import android.content.Context; import android.graphics.drawable.Drawable; @@ -22,6 +25,7 @@ import android.text.TextUtils.TruncateAt; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; @@ -35,6 +39,13 @@ public class ScrollingTabContainerView extends HorizontalScrollView { int mMaxTabWidth; + protected Animator mVisibilityAnim; + protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener(); + + private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator(); + + private static final int FADE_DURATION = 200; + public ScrollingTabContainerView(Context context) { super(context); setHorizontalScrollBarEnabled(false); @@ -76,6 +87,30 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } + public void animateToVisibility(int visibility) { + if (mVisibilityAnim != null) { + mVisibilityAnim.cancel(); + } + if (visibility == VISIBLE) { + if (getVisibility() != VISIBLE) { + setAlpha(0); + } + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1); + anim.setDuration(FADE_DURATION); + anim.setInterpolator(sAlphaInterpolator); + + anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); + anim.start(); + } else { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0); + anim.setDuration(FADE_DURATION); + anim.setInterpolator(sAlphaInterpolator); + + anim.addListener(mVisAnimListener.withFinalVisibility(visibility)); + anim.start(); + } + } + public void animateToTab(int position) { final View tabView = mTabLayout.getChildAt(position); if (mTabSelector != null) { @@ -259,4 +294,38 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } } } + + protected class VisibilityAnimListener implements Animator.AnimatorListener { + private boolean mCanceled = false; + private int mFinalVisibility; + + public VisibilityAnimListener withFinalVisibility(int visibility) { + mFinalVisibility = visibility; + return this; + } + + @Override + public void onAnimationStart(Animator animation) { + setVisibility(VISIBLE); + mVisibilityAnim = animation; + mCanceled = false; + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mCanceled) return; + + mVisibilityAnim = null; + setVisibility(mFinalVisibility); + } + + @Override + public void onAnimationCancel(Animator animation) { + mCanceled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + } } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index 5b3510428f7b..04bb6892c1ae 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -20,7 +20,7 @@ import java.util.ArrayList; import android.animation.Animator; import android.animation.Animator.AnimatorListener; -import android.animation.ObjectAnimator; +import android.animation.AnimatorListenerAdapter; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; @@ -43,9 +43,9 @@ import com.android.internal.R; * A special widget containing a center and outer ring. Moving the center ring to the outer ring * causes an event that can be caught by implementing OnTriggerListener. */ -public class MultiWaveView extends View implements AnimatorUpdateListener { +public class MultiWaveView extends View { private static final String TAG = "MultiWaveView"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; // Wave state machine private static final int STATE_IDLE = 0; @@ -67,14 +67,15 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } // Tune-able parameters - private static final int CHEVRON_INCREMENTAL_DELAY = 50; - private static final int CHEVRON_ANIMATION_DURATION = 1000; - private static final int RETURN_TO_HOME_DURATION = 150; - private static final int HIDE_ANIMATION_DELAY = 500; - private static final int HIDE_ANIMATION_DURACTION = 2000; + private static final int CHEVRON_INCREMENTAL_DELAY = 160; + private static final int CHEVRON_ANIMATION_DURATION = 650; + private static final int RETURN_TO_HOME_DELAY = 1200; + private static final int RETURN_TO_HOME_DURATION = 300; + private static final int HIDE_ANIMATION_DELAY = 200; + private static final int HIDE_ANIMATION_DURATION = RETURN_TO_HOME_DELAY; private static final int SHOW_ANIMATION_DURATION = 0; private static final int SHOW_ANIMATION_DELAY = 0; - private TimeInterpolator mChevronAnimationInterpolator = Ease.Quint.easeOut; + private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut; private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>(); private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>(); @@ -99,14 +100,31 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { private float mHitRadius = 0.0f; private float mSnapMargin = 0.0f; private boolean mDragging; + private int mNewTargetResources; - private AnimatorListener mResetListener = new Animator.AnimatorListener() { - public void onAnimationStart(Animator animation) { } - public void onAnimationRepeat(Animator animation) { } - public void onAnimationEnd(Animator animation) { + private AnimatorListener mResetListener = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); } - public void onAnimationCancel(Animator animation) { } + }; + + private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() { + public void onAnimationUpdate(ValueAnimator animation) { + invalidateGlobalRegion(mHandleDrawable); + invalidate(); + } + }; + + private boolean mAnimatingTargets; + private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() { + public void onAnimationEnd(Animator animator) { + if (mNewTargetResources != 0) { + internalSetTargetResources(mNewTargetResources); + mNewTargetResources = 0; + hideTargets(false); + } + mAnimatingTargets = false; + } }; public MultiWaveView(Context context) { @@ -135,31 +153,23 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mOuterRing = new TargetDrawable(res, a.getDrawable(R.styleable.MultiWaveView_waveDrawable)); // Read chevron animation drawables - Drawable leftChevron = a.getDrawable(R.styleable.MultiWaveView_leftChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - leftChevron != null ? new TargetDrawable(res, leftChevron) : null); - } - Drawable rightChevron = a.getDrawable(R.styleable.MultiWaveView_rightChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - rightChevron != null ? new TargetDrawable(res, rightChevron) : null); - } - Drawable topChevron = a.getDrawable(R.styleable.MultiWaveView_topChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - topChevron != null ? new TargetDrawable(res, topChevron) : null); - } - Drawable bottomChevron = a.getDrawable(R.styleable.MultiWaveView_bottomChevronDrawable); - for (int i = 0; i < mFeedbackCount; i++) { - mChevronDrawables.add( - bottomChevron != null ? new TargetDrawable(res, bottomChevron) : null); + final int chevrons[] = { R.styleable.MultiWaveView_leftChevronDrawable, + R.styleable.MultiWaveView_rightChevronDrawable, + R.styleable.MultiWaveView_topChevronDrawable, + R.styleable.MultiWaveView_bottomChevronDrawable + }; + for (int chevron : chevrons) { + Drawable chevronDrawable = a.getDrawable(chevron); + for (int i = 0; i < mFeedbackCount; i++) { + mChevronDrawables.add( + chevronDrawable != null ? new TargetDrawable(res, chevronDrawable) : null); + } } // Read array of target drawables TypedValue outValue = new TypedValue(); if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) { - setTargetResources(outValue.resourceId); + internalSetTargetResources(outValue.resourceId); } if (mTargetDrawables == null || mTargetDrawables.size() == 0) { throw new IllegalStateException("Must specify at least one target drawable"); @@ -205,7 +215,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { case STATE_FIRST_TOUCH: stopHandleAnimation(); deactivateTargets(); - showTargets(); + showTargets(true); mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE); setGrabbedState(OnTriggerListener.CENTER_HANDLE); break; @@ -228,17 +238,18 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { * mFeedbackCount items in the order: left, right, top, bottom. */ private void startChevronAnimation() { - final float r = mHandleDrawable.getWidth() / 2; + final float r = mHandleDrawable.getWidth() * 0.4f; + final float chevronAnimationDistance = mOuterRadius * 0.8f; final float from[][] = { {mWaveCenterX - r, mWaveCenterY}, // left {mWaveCenterX + r, mWaveCenterY}, // right {mWaveCenterX, mWaveCenterY - r}, // top {mWaveCenterX, mWaveCenterY + r} }; // bottom final float to[][] = { - {mWaveCenterX - mOuterRadius, mWaveCenterY}, // left - {mWaveCenterX + mOuterRadius, mWaveCenterY}, // right - {mWaveCenterX, mWaveCenterY - mOuterRadius}, // top - {mWaveCenterX, mWaveCenterY + mOuterRadius} }; // bottom + {mWaveCenterX - chevronAnimationDistance, mWaveCenterY}, // left + {mWaveCenterX + chevronAnimationDistance, mWaveCenterY}, // right + {mWaveCenterX, mWaveCenterY - chevronAnimationDistance}, // top + {mWaveCenterX, mWaveCenterY + chevronAnimationDistance} }; // bottom mChevronAnimations.clear(); for (int direction = 0; direction < 4; direction++) { @@ -254,7 +265,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { "x", new float[] { from[direction][0], to[direction][0] }, "y", new float[] { from[direction][1], to[direction][1] }, "alpha", new float[] {1.0f, 0.0f}, - "onUpdate", this)); + "onUpdate", mUpdateListener)); } } } @@ -308,70 +319,109 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } private void doFinish() { - // Inform listener of any active targets. Typically only one will be active. final int activeTarget = mActiveTarget; boolean targetHit = activeTarget != -1; - if (targetHit) { - Log.v(TAG, "Finish with target hit = " + targetHit); - dispatchTriggerEvent(mActiveTarget); - } - - setGrabbedState(OnTriggerListener.NO_HANDLE); - - // Animate finger outline back to home position - mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f); - mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION, - "ease", Ease.Quart.easeOut, - "delay", targetHit ? HIDE_ANIMATION_DELAY : 0, - "alpha", 1.0f, - "x", mWaveCenterX, - "y", mWaveCenterY, - "onUpdate", this, - "onComplete", mResetListener); // Hide unselected targets hideTargets(true); // Highlight the selected one + mHandleDrawable.setAlpha(targetHit ? 0.0f : 1.0f); if (targetHit) { mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE); + + hideUnselected(activeTarget); + + // Inform listener of any active targets. Typically only one will be active. + if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit); + dispatchTriggerEvent(mActiveTarget); + mHandleAnimation = Tweener.to(mHandleDrawable, 0, + "ease", Ease.Quart.easeOut, + "delay", RETURN_TO_HOME_DELAY, + "alpha", 1.0f, + "x", mWaveCenterX, + "y", mWaveCenterY, + "onUpdate", mUpdateListener, + "onComplete", mResetListener); + } else { + // Animate finger outline back to home position + mHandleAnimation = Tweener.to(mHandleDrawable, RETURN_TO_HOME_DURATION, + "ease", Ease.Quart.easeOut, + "delay", 0, + "alpha", 1.0f, + "x", mWaveCenterX, + "y", mWaveCenterY, + "onUpdate", mUpdateListener, + "onComplete", mResetListener); } + + setGrabbedState(OnTriggerListener.NO_HANDLE); + } + + private void hideUnselected(int active) { + for (int i = 0; i < mTargetDrawables.size(); i++) { + if (i != active) { + mTargetDrawables.get(i).setAlpha(0.0f); + } + } + mOuterRing.setAlpha(0.0f); } private void hideTargets(boolean animate) { if (mTargetAnimations.size() > 0) { stopTargetAnimation(); } - for (TargetDrawable target : mTargetDrawables) { - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, - animate ? HIDE_ANIMATION_DURACTION : 0, + // Note: these animations should complete at the same time so that we can swap out + // the target assets asynchronously from the setTargetResources() call. + mAnimatingTargets = animate; + if (animate) { + final int duration = animate ? HIDE_ANIMATION_DURATION : 0; + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + mTargetAnimations.add(Tweener.to(target, duration, + "alpha", 0.0f, + "delay", HIDE_ANIMATION_DELAY, + "onUpdate", mUpdateListener)); + } + mTargetAnimations.add(Tweener.to(mOuterRing, duration, "alpha", 0.0f, "delay", HIDE_ANIMATION_DELAY, - "onUpdate", this)); + "onUpdate", mUpdateListener, + "onComplete", mTargetUpdateListener)); + } else { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + target.setAlpha(0.0f); + } + mOuterRing.setAlpha(0.0f); } - mTargetAnimations.add(Tweener.to(mOuterRing, - animate ? HIDE_ANIMATION_DURACTION : 0, - "alpha", 0.0f, - "delay", HIDE_ANIMATION_DELAY, - "onUpdate", this)); } - private void showTargets() { + private void showTargets(boolean animate) { if (mTargetAnimations.size() > 0) { stopTargetAnimation(); } - for (TargetDrawable target : mTargetDrawables) { - target.setState(TargetDrawable.STATE_INACTIVE); - mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION, + mAnimatingTargets = animate; + if (animate) { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + mTargetAnimations.add(Tweener.to(target, SHOW_ANIMATION_DURATION, + "alpha", 1.0f, + "delay", SHOW_ANIMATION_DELAY, + "onUpdate", mUpdateListener)); + } + mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION, "alpha", 1.0f, "delay", SHOW_ANIMATION_DELAY, - "onUpdate", this)); + "onUpdate", mUpdateListener, + "onComplete", mTargetUpdateListener)); + } else { + for (TargetDrawable target : mTargetDrawables) { + target.setState(TargetDrawable.STATE_INACTIVE); + target.setAlpha(1.0f); + } + mOuterRing.setAlpha(1.0f); } - mTargetAnimations.add(Tweener.to(mOuterRing, SHOW_ANIMATION_DURATION, - "alpha", 1.0f, - "delay", SHOW_ANIMATION_DELAY, - "onUpdate", this)); } private void stopTargetAnimation() { @@ -387,12 +437,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } } - /** - * Loads an array of drawables from the given resourceId. - * - * @param resourceId - */ - public void setTargetResources(int resourceId) { + private void internalSetTargetResources(int resourceId) { Resources res = getContext().getResources(); TypedArray array = res.obtainTypedArray(resourceId); int count = array.length(); @@ -402,6 +447,21 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { targetDrawables.add(new TargetDrawable(res, drawable)); } mTargetDrawables = targetDrawables; + updateTargetPositions(); + } + + /** + * Loads an array of drawables from the given resourceId. + * + * @param resourceId + */ + public void setTargetResources(int resourceId) { + if (mAnimatingTargets) { + // postpone this change until we return to the initial state + mNewTargetResources = resourceId; + } else { + internalSetTargetResources(resourceId); + } } /** @@ -590,20 +650,9 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { } } - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - final int width = right - left; - final int height = bottom - top; - - mWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2; - mWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2; - moveHandleTo(mWaveCenterX, mWaveCenterY, false); - mOuterRing.setX(mWaveCenterX); - mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY)); - mOuterRing.setAlpha(0.0f); + private void performInitialLayout(float centerX, float centerY) { if (mOuterRadius == 0.0f) { - mOuterRadius = 0.5f*(float) Math.sqrt(dist2(mWaveCenterX, mWaveCenterY)); + mOuterRadius = 0.5f*(float) Math.sqrt(dist2(centerX, centerY)); } if (mHitRadius == 0.0f) { // Use the radius of inscribed circle of the first target. @@ -613,6 +662,35 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics()); } + hideChevrons(); + hideTargets(false); + moveHandleTo(centerX, centerY, false); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + final int width = right - left; + final int height = bottom - top; + float newWaveCenterX = mHorizontalOffset + Math.max(width, mOuterRing.getWidth() ) / 2; + float newWaveCenterY = mVerticalOffset + Math.max(height, mOuterRing.getHeight()) / 2; + if (newWaveCenterX != mWaveCenterX || newWaveCenterY != mWaveCenterY) { + if (mWaveCenterX == 0 && mWaveCenterY == 0) { + performInitialLayout(newWaveCenterX, newWaveCenterY); + } + mWaveCenterX = newWaveCenterX; + mWaveCenterY = newWaveCenterY; + + mOuterRing.setX(mWaveCenterX); + mOuterRing.setY(Math.max(mWaveCenterY, mWaveCenterY)); + + updateTargetPositions(); + } + if (DEBUG) dump(); + } + + private void updateTargetPositions() { + // Reposition the target drawables if the view changed. for (int i = 0; i < mTargetDrawables.size(); i++) { final TargetDrawable targetIcon = mTargetDrawables.get(i); double angle = -2.0f * Math.PI * i / mTargetDrawables.size(); @@ -620,11 +698,7 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { float yPosition = mWaveCenterY + mOuterRadius * (float) Math.sin(angle); targetIcon.setX(xPosition); targetIcon.setY(yPosition); - targetIcon.setAlpha(0.0f); } - hideChevrons(); - hideTargets(false); - if (DEBUG) dump(); } private void hideChevrons() { @@ -655,11 +729,6 @@ public class MultiWaveView extends View implements AnimatorUpdateListener { mOnTriggerListener = listener; } - public void onAnimationUpdate(ValueAnimator animation) { - invalidateGlobalRegion(mHandleDrawable); - invalidate(); - } - private float square(float d) { return d * d; } diff --git a/core/res/res/layout-land/ssl_certificate.xml b/core/res/res/layout-land/ssl_certificate.xml index 56e4e70ca033..c3e6deb832e3 100644 --- a/core/res/res/layout-land/ssl_certificate.xml +++ b/core/res/res/layout-land/ssl_certificate.xml @@ -20,6 +20,7 @@ android:layout_height="wrap_content" > <LinearLayout + android:id="@+id/body" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > diff --git a/core/res/res/layout/ssl_certificate.xml b/core/res/res/layout/ssl_certificate.xml index 7206077ce6f5..ae661ce8ef67 100644 --- a/core/res/res/layout/ssl_certificate.xml +++ b/core/res/res/layout/ssl_certificate.xml @@ -20,6 +20,7 @@ android:layout_height="wrap_content" > <LinearLayout + android:id="@+id/body" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index fd61cfdaf6b7..db33d1c4e19f 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2208,7 +2208,8 @@ <!-- The event types this serivce would like to receive as specified in {@link android.view.accessibility.AccessibilityEvent}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityEventTypes"> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED} events.--> <flag name="typeViewClicked" value="0x00000001" /> @@ -2232,17 +2233,24 @@ <flag name="typeTouchExplorationGestureStart" value="0x00000200" /> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END} events. --> <flag name="typeTouchExplorationGestureEnd" value="0x00000400" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events. --> + <flag name="typeWindowContentChanged" value="0x00000800" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED} events. --> + <flag name="typeViewScrolled" value="0x000001000" /> + <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED} events. --> + <flag name="typeViewTextSelectionChanged" value="0x000002000" /> <!-- Receives {@link android.view.accessibility.AccessibilityEvent#TYPES_ALL_MASK} i.e. all events. --> <flag name="typeAllMask" value="0xffffffff" /> </attr> <!-- Comma separated package names from which this serivce would like to receive events (leave out for all packages). - This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="packageNames" format="string" /> <!-- The feedback types this serivce provides as specified in {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityFeedbackType"> <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_SPOKEN} feedback. --> <flag name="feedbackSpoken" value="0x00000001" /> @@ -2255,14 +2263,16 @@ <!-- Provides {@link android.accessibilityservice.AccessibilityServiceInfo#FEEDBACK_GENERIC} feedback. --> <flag name="feedbackGeneric" value="0x00000010" /> </attr> - <!-- The minimal period in milliseconds between two accessibility events are sent - to this serivce. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + <!-- The minimal period in milliseconds between two accessibility events of the same type + are sent to this serivce. This setting can be changed at runtime by calling + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. -->> <attr name="notificationTimeout" format="integer" /> <!-- Additional flags as specified in {@link android.accessibilityservice.AccessibilityServiceInfo}. This setting can be changed at runtime by calling - {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> + {@link android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo) + android.accessibilityservice.AccessibilityService.setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. --> <attr name="accessibilityFlags"> <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#DEFAULT} --> <flag name="flagDefault" value="0x00000001" /> @@ -2271,7 +2281,7 @@ the settings for this service. This setting cannot be changed at runtime. --> <attr name="settingsActivity" /> <!-- Flag whether the accessibility service wants to be able to retrieve the - focused window content. This setting cannot be changed at runtime. --> + active window content. This setting cannot be changed at runtime. --> <attr name="canRetrieveWindowContent" format="boolean" /> </declare-styleable> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 827153e7243e..2c10b3dabe8f 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -658,4 +658,8 @@ This is intended to allow packaging drivers or tools for installation on a PC. --> <string translatable="false" name="config_isoImagePath"></string> + <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be + autodetected from the Configuration. --> + <bool name="config_showNavigationBar">false</bool> + </resources> diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index c82fb9b77ed4..e36360c167a8 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -139,7 +139,7 @@ public: // setFrameAvailableListener sets the listener object that will be notified // when a new frame becomes available. - void setFrameAvailableListener(const sp<FrameAvailableListener>& l); + void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); // getAllocator retrieves the binder object that must be referenced as long // as the GraphicBuffers dequeued from this SurfaceTexture are referenced. @@ -343,7 +343,7 @@ private: uint32_t mNextTransform; // mTexName is the name of the OpenGL texture to which streamed images will - // be bound when updateTexImage is called. It is set at construction time + // be bound when updateTexImage is called. It is set at construction time // changed with a call to setTexName. const GLuint mTexName; diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index 0925001965dd..3bf6477cf208 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -148,6 +148,11 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) { LOGV("SurfaceTexture::setBufferCount"); Mutex::Autolock lock(mMutex); + if (bufferCount > NUM_BUFFER_SLOTS) { + LOGE("setBufferCount: bufferCount larger than slots available"); + return BAD_VALUE; + } + // Error out if the user has dequeued buffers for (int i=0 ; i<mBufferCount ; i++) { if (mSlots[i].mBufferState == BufferSlot::DEQUEUED) { @@ -208,7 +213,7 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { LOGV("SurfaceTexture::dequeueBuffer"); - if ((w && !h) || (!w & h)) { + if ((w && !h) || (!w && h)) { LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } @@ -699,10 +704,10 @@ nsecs_t SurfaceTexture::getTimestamp() { } void SurfaceTexture::setFrameAvailableListener( - const sp<FrameAvailableListener>& l) { + const sp<FrameAvailableListener>& listener) { LOGV("SurfaceTexture::setFrameAvailableListener"); Mutex::Autolock lock(mMutex); - mFrameAvailableListener = l; + mFrameAvailableListener = listener; } sp<IBinder> SurfaceTexture::getAllocator() { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 165683e986b6..f1a2a60b9495 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -408,13 +408,20 @@ rinse_repeat: if (firstTime) { Mutex::Autolock autoLock(mLock); - int32_t targetDuration; - if (!mPlaylist->isComplete() - || !mPlaylist->meta()->findInt32( - "target-duration", &targetDuration)) { + if (!mPlaylist->isComplete()) { mDurationUs = -1; } else { - mDurationUs = 1000000ll * targetDuration * mPlaylist->size(); + mDurationUs = 0; + for (size_t i = 0; i < mPlaylist->size(); ++i) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt( + i, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + mDurationUs += itemDurationUs; + } } } @@ -431,14 +438,26 @@ rinse_repeat: bool bandwidthChanged = false; if (mSeekTimeUs >= 0) { - int32_t targetDuration; - if (mPlaylist->isComplete() && - mPlaylist->meta()->findInt32( - "target-duration", &targetDuration)) { - int64_t seekTimeSecs = (mSeekTimeUs + 500000ll) / 1000000ll; - int64_t index = seekTimeSecs / targetDuration; - - if (index >= 0 && index < mPlaylist->size()) { + if (mPlaylist->isComplete()) { + size_t index = 0; + int64_t segmentStartUs = 0; + while (index < mPlaylist->size()) { + sp<AMessage> itemMeta; + CHECK(mPlaylist->itemAt( + index, NULL /* uri */, &itemMeta)); + + int64_t itemDurationUs; + CHECK(itemMeta->findInt64("durationUs", &itemDurationUs)); + + if (mSeekTimeUs < segmentStartUs + itemDurationUs) { + break; + } + + segmentStartUs += itemDurationUs; + ++index; + } + + if (index < mPlaylist->size()) { int32_t newSeqNumber = firstSeqNumberInPlaylist + index; if (newSeqNumber != mSeqNumber) { diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp index 765f79565d5a..123fbf8b23e4 100644 --- a/media/libstagefright/httplive/M3UParser.cpp +++ b/media/libstagefright/httplive/M3UParser.cpp @@ -64,14 +64,21 @@ size_t M3UParser::size() { } bool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) { - uri->clear(); - if (meta) { *meta = NULL; } + if (uri) { + uri->clear(); + } + + if (meta) { + *meta = NULL; + } if (index >= mItems.size()) { return false; } - *uri = mItems.itemAt(index).mURI; + if (uri) { + *uri = mItems.itemAt(index).mURI; + } if (meta) { *meta = mItems.itemAt(index).mMeta; diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 7a4ac5d816f6..5298f2e66927 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -36,10 +36,6 @@ <!-- Whether or not we show the number in the bar. --> <bool name="config_statusBarShowNumber">true</bool> - <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be - autodetected from the Configuration. --> - <bool name="config_showNavigationBar">false</bool> - <!-- How many icons may be shown at once in the system bar. Includes any slots that may be reused for things like IME control. --> <integer name="config_maxNotificationIcons">5</integer> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index d8474db3c2ef..4c7b0dd2ca54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -246,7 +246,7 @@ public class PhoneStatusBar extends StatusBar { mIntruderAlertView.setClickable(true); try { - boolean showNav = res.getBoolean(R.bool.config_showNavigationBar); + boolean showNav = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar); if (showNav) { mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index 8b7a61e21ce7..1c4084c77cd1 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -64,7 +64,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, private KeyguardUpdateMonitor mUpdateMonitor; private KeyguardScreenCallback mCallback; - private SlidingTab mSlidingTab; private TextView mScreenLocked; private TextView mEmergencyCallText; private Button mEmergencyCallButton; @@ -89,11 +88,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, private boolean mEnableMenuKeyInLockScreen; private StatusView mStatusView; - private WaveView mEnergyWave; - private SlidingTabMethods mSlidingTabMethods; - private WaveViewMethods mWaveViewMethods; - private MultiWaveView mMultiWaveView; - private MultiWaveViewMethods mMultiWaveViewMethods; + private UnlockWidgetCommonMethods mUnlockWidgetMethods; + private View mUnlockWidget; + /** * The status of this lock screen. @@ -151,9 +148,28 @@ class LockScreen extends LinearLayout implements KeyguardScreen, } } - class SlidingTabMethods implements SlidingTab.OnTriggerListener { + private interface UnlockWidgetCommonMethods { + // Update resources based on phone state + public void updateResources(); + + // Get the view associated with this widget + public View getView(); - private void updateRightTabResources() { + // Reset the view + public void reset(boolean animate); + + // Animate the widget if it supports ping() + public void ping(); + } + + class SlidingTabMethods implements SlidingTab.OnTriggerListener, UnlockWidgetCommonMethods { + private final SlidingTab mSlidingTab; + + SlidingTabMethods(SlidingTab slidingTab) { + mSlidingTab = slidingTab; + } + + public void updateResources() { boolean vibe = mSilentMode && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); @@ -175,7 +191,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.goToUnlockScreen(); } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { toggleRingMode(); - updateRightTabResources(); doSilenceRingToast(); mCallback.pokeWakelock(); } @@ -195,12 +210,29 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(); } } + + public View getView() { + return mSlidingTab; + } + + public void reset(boolean animate) { + mSlidingTab.reset(animate); + } + + public void ping() { + } } private static final int WAIT_FOR_ANIMATION_TIMEOUT = 0; private static final int STAY_ON_WHILE_GRABBED_TIMEOUT = 30000; - class WaveViewMethods implements WaveView.OnTriggerListener { + class WaveViewMethods implements WaveView.OnTriggerListener, UnlockWidgetCommonMethods { + + private final WaveView mWaveView; + + WaveViewMethods(WaveView waveView) { + mWaveView = waveView; + } /** {@inheritDoc} */ public void onTrigger(View v, int whichHandle) { if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) { @@ -210,8 +242,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { - if (DBG) Log.v(TAG, "*** LockScreen accel is " - + (mEnergyWave.isHardwareAccelerated() ? "on":"off")); // Don't poke the wake lock when returning to a state where the handle is // not grabbed since that can happen when the system (instead of the user) // cancels the grab. @@ -219,30 +249,51 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); } } + + public void updateResources() { + } + + public View getView() { + return mWaveView; + } + public void reset(boolean animate) { + mWaveView.reset(); + } + public void ping() { + } } - class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener { + class MultiWaveViewMethods implements MultiWaveView.OnTriggerListener, + UnlockWidgetCommonMethods { + + private final MultiWaveView mMultiWaveView; + + MultiWaveViewMethods(MultiWaveView multiWaveView) { + mMultiWaveView = multiWaveView; + } + + public void updateResources() { + mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent + : R.array.lockscreen_targets_when_soundon); + } + public void onGrabbed(View v, int handle) { } + public void onReleased(View v, int handle) { } + public void onTrigger(View v, int target) { if (target == 0) { // TODO: Use resources to determine which handle was used mCallback.goToUnlockScreen(); } else if (target == 2) { toggleRingMode(); - updateResources(); doSilenceRingToast(); + mUnlockWidgetMethods.updateResources(); mCallback.pokeWakelock(); } - - } - - private void updateResources() { - mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent - : R.array.lockscreen_targets_when_soundon); } public void onGrabbedStateChange(View v, int handle) { @@ -253,6 +304,18 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mCallback.pokeWakelock(); } } + + public View getView() { + return mMultiWaveView; + } + + public void reset(boolean animate) { + mMultiWaveView.reset(animate); + } + + public void ping() { + mMultiWaveView.ping(); + } } private void requestUnlockScreen() { @@ -371,32 +434,39 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mSilentMode = isSilentMode(); - View unlockWidget = findViewById(R.id.unlock_widget); - if (unlockWidget instanceof SlidingTab) { - mSlidingTab = (SlidingTab) unlockWidget; - mSlidingTab.setHoldAfterTrigger(true, false); - mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label); - mSlidingTab.setLeftTabResources( + mUnlockWidget = findViewById(R.id.unlock_widget); + if (mUnlockWidget instanceof SlidingTab) { + SlidingTab slidingTabView = (SlidingTab) mUnlockWidget; + slidingTabView.setHoldAfterTrigger(true, false); + slidingTabView.setLeftHintText(R.string.lockscreen_unlock_label); + slidingTabView.setLeftTabResources( R.drawable.ic_jog_dial_unlock, R.drawable.jog_tab_target_green, R.drawable.jog_tab_bar_left_unlock, R.drawable.jog_tab_left_unlock); - mSlidingTabMethods = new SlidingTabMethods(); - mSlidingTab.setOnTriggerListener(mSlidingTabMethods); - mSlidingTabMethods.updateRightTabResources(); - } else if (unlockWidget instanceof WaveView) { - mEnergyWave = (WaveView) unlockWidget; - mWaveViewMethods = new WaveViewMethods(); - mEnergyWave.setOnTriggerListener(mWaveViewMethods); - } else if (unlockWidget instanceof MultiWaveView) { - mMultiWaveView = (MultiWaveView) unlockWidget; - mMultiWaveViewMethods = new MultiWaveViewMethods(); - mMultiWaveViewMethods.updateResources(); // update silence/ring resources - mMultiWaveView.setOnTriggerListener(mMultiWaveViewMethods); + SlidingTabMethods slidingTabMethods = new SlidingTabMethods(slidingTabView); + slidingTabView.setOnTriggerListener(slidingTabMethods); + mUnlockWidgetMethods = slidingTabMethods; + } else if (mUnlockWidget instanceof WaveView) { + WaveView waveView = (WaveView) mUnlockWidget; + WaveViewMethods waveViewMethods = new WaveViewMethods(waveView); + waveView.setOnTriggerListener(waveViewMethods); + mUnlockWidgetMethods = waveViewMethods; + } else if (mUnlockWidget instanceof MultiWaveView) { + MultiWaveView multiWaveView = (MultiWaveView) mUnlockWidget; + MultiWaveViewMethods multiWaveViewMethods = new MultiWaveViewMethods(multiWaveView); + multiWaveView.setOnTriggerListener(multiWaveViewMethods); + mUnlockWidgetMethods = multiWaveViewMethods; } else { - throw new IllegalStateException("Unrecognized unlock widget: " + unlockWidget); + throw new IllegalStateException("Unrecognized unlock widget: " + mUnlockWidget); } + // Update widget with initial ring state + mUnlockWidgetMethods.updateResources(); + + if (DBG) Log.v(TAG, "*** LockScreen accel is " + + (mUnlockWidget.isHardwareAccelerated() ? "on":"off")); + resetStatusInfo(updateMonitor); } @@ -540,16 +610,14 @@ class LockScreen extends LinearLayout implements KeyguardScreen, * Enables unlocking of this screen. Typically just shows the unlock widget. */ private void enableUnlock() { - if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE); - if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE); + mUnlockWidgetMethods.getView().setVisibility(View.VISIBLE); } /** * Disable unlocking of this screen. Typically just hides the unlock widget. */ private void disableUnlock() { - if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE); - if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE); + mUnlockWidgetMethods.getView().setVisibility(View.GONE); } /** @@ -728,20 +796,13 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onPause() { - if (mEnergyWave != null) { - mEnergyWave.reset(); - } - if (mMultiWaveView != null) { - mMultiWaveView.reset(false); - } + mUnlockWidgetMethods.reset(false); } /** {@inheritDoc} */ public void onResume() { resetStatusInfo(mUpdateMonitor); - if (mMultiWaveView != null) { - mMultiWaveView.ping(); - } + mUnlockWidgetMethods.ping(); } /** {@inheritDoc} */ @@ -757,7 +818,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen, boolean silent = AudioManager.RINGER_MODE_NORMAL != state; if (silent != mSilentMode) { mSilentMode = silent; - if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources(); + mUnlockWidgetMethods.updateResources(); } } diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java index 1d3e3ac23c8b..b3d72200909c 100644 --- a/services/java/com/android/server/IntentResolver.java +++ b/services/java/com/android/server/IntentResolver.java @@ -41,7 +41,7 @@ import android.content.IntentFilter; /** * {@hide} */ -public class IntentResolver<F extends IntentFilter, R extends Object> { +public abstract class IntentResolver<F extends IntentFilter, R extends Object> { final private static String TAG = "IntentResolver"; final private static boolean DEBUG = false; final private static boolean localLOGV = DEBUG || false; @@ -333,14 +333,19 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { return false; } - protected String packageForFilter(F filter) { - return null; - } + /** + * Return the package that owns this filter. This must be implemented to + * provide correct filtering of Intents that have specified a package name + * they are to be delivered to. + */ + protected abstract String packageForFilter(F filter); + @SuppressWarnings("unchecked") protected R newResult(F filter, int match) { return (R)filter; } + @SuppressWarnings("unchecked") protected void sortResults(List<R> results) { Collections.sort(results, mResolvePrioritySorter); } @@ -502,6 +507,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { String resolvedType, String scheme, List<F> src, List<R> dest) { final String action = intent.getAction(); final Uri data = intent.getData(); + final String packageName = intent.getPackage(); final boolean excludingStopped = intent.isExcludingStopped(); @@ -520,6 +526,14 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { continue; } + // Is delivery being limited to filters owned by a particular package? + if (packageName != null && !packageName.equals(packageForFilter(filter))) { + if (debug) { + Slog.v(TAG, " Filter is not from package " + packageName + "; skipping"); + } + continue; + } + // Do we already have this one? if (!allowFilterResult(filter, dest)) { if (debug) { @@ -561,6 +575,7 @@ public class IntentResolver<F extends IntentFilter, R extends Object> { } // Sorts a List of IntentFilter objects into descending priority order. + @SuppressWarnings("rawtypes") private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { final int q1 = ((IntentFilter) o1).getPriority(); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 656ec4d266dd..56afe7f51414 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -195,6 +195,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final Object mKey; final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>(); int mPendingBroadcasts; + String requiredPermissions; Receiver(ILocationListener listener) { mListener = listener; @@ -284,7 +285,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -319,7 +321,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler); + mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -358,7 +361,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() - mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler); + mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, + requiredPermissions); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -572,22 +576,30 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return Settings.Secure.isLocationProviderEnabled(resolver, provider); } - private void checkPermissionsSafe(String provider) { - if ((LocationManager.GPS_PROVIDER.equals(provider) - || LocationManager.PASSIVE_PROVIDER.equals(provider)) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION permission"); + private String checkPermissionsSafe(String provider, String lastPermission) { + if (LocationManager.GPS_PROVIDER.equals(provider) + || LocationManager.PASSIVE_PROVIDER.equals(provider)) { + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION permission"); + } + return ACCESS_FINE_LOCATION; } - if (LocationManager.NETWORK_PROVIDER.equals(provider) - && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) - != PackageManager.PERMISSION_GRANTED) - && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) - != PackageManager.PERMISSION_GRANTED)) { - throw new SecurityException("Provider " + provider - + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); + + // Assume any other provider requires the coarse or fine permission. + if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION.equals(lastPermission) + ? lastPermission : ACCESS_COARSE_LOCATION; } + if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return ACCESS_FINE_LOCATION; + } + + throw new SecurityException("Provider " + provider + + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission"); } private boolean isAllowedProviderSafe(String provider) { @@ -1099,8 +1111,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } } + void validatePendingIntent(PendingIntent intent) { + if (intent.isTargetedToPackage()) { + return; + } + Slog.i(TAG, "Given Intent does not require a specific package: " + + intent); + // XXX we should really throw a security exception, if the caller's + // targetSdkVersion is high enough. + //throw new SecurityException("Given Intent does not require a specific package: " + // + intent); + } + public void requestLocationUpdatesPI(String provider, Criteria criteria, long minTime, float minDistance, boolean singleShot, PendingIntent intent) { + validatePendingIntent(intent); if (criteria != null) { // FIXME - should we consider using multiple providers simultaneously // rather than only the best one? @@ -1132,7 +1157,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run throw new IllegalArgumentException("provider=" + provider); } - checkPermissionsSafe(provider); + receiver.requiredPermissions = checkPermissionsSafe(provider, + receiver.requiredPermissions); // so wakelock calls will succeed final int callingUid = Binder.getCallingUid(); @@ -1300,7 +1326,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } // first check for permission to the provider - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1432,7 +1458,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, enteredIntent, this, mLocationHandler); + intent.send(mContext, 0, enteredIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1457,7 +1484,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run synchronized (this) { // synchronize to ensure incrementPendingBroadcasts() // is called before decrementPendingBroadcasts() - intent.send(mContext, 0, exitedIntent, this, mLocationHandler); + intent.send(mContext, 0, exitedIntent, this, mLocationHandler, + ACCESS_FINE_LOCATION); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcasts(); @@ -1526,6 +1554,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void addProximityAlert(double latitude, double longitude, float radius, long expiration, PendingIntent intent) { + validatePendingIntent(intent); try { synchronized (mLock) { addProximityAlertLocked(latitude, longitude, radius, expiration, intent); @@ -1626,7 +1655,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); Bundle b = new Bundle(); b.putBoolean("network", p.requiresNetwork()); @@ -1668,7 +1697,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private boolean _isProviderEnabledLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { @@ -1694,7 +1723,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } private Location _getLastKnownLocationLocked(String provider) { - checkPermissionsSafe(provider); + checkPermissionsSafe(provider, null); LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4ec71c180e87..bf877f6e9d20 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -622,6 +622,11 @@ public final class ActivityManagerService extends ActivityManagerNative } return true; } + + @Override + protected String packageForFilter(BroadcastFilter filter) { + return filter.packageName; + } }; /** @@ -1825,6 +1830,8 @@ public final class ActivityManagerService extends ActivityManagerNative // We already have the app running, or are waiting for it to // come up (we have a pid but not yet its thread), so keep it. if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app); + // If this is a new package in the process, add the package to the list + app.addPackage(info.packageName); return app; } else { // An application record is attached to a previous process, @@ -2278,7 +2285,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - return pir.sendInner(0, fillInIntent, resolvedType, + return pir.sendInner(0, fillInIntent, resolvedType, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues); } @@ -4162,6 +4169,27 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } + public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) { + if (!(pendingResult instanceof PendingIntentRecord)) { + return false; + } + try { + PendingIntentRecord res = (PendingIntentRecord)pendingResult; + if (res.key.allIntents == null) { + return false; + } + for (int i=0; i<res.key.allIntents.length; i++) { + Intent intent = res.key.allIntents[i]; + if (intent.getPackage() != null && intent.getComponent() != null) { + return false; + } + } + return true; + } catch (ClassCastException e) { + } + return false; + } + public void setProcessLimit(int max) { enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT, "setProcessLimit()"); @@ -9895,6 +9923,7 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(r.appInfo.packageName); realStartServiceLocked(r, app); return true; } catch (RemoteException e) { @@ -10945,7 +10974,7 @@ public final class ActivityManagerService extends ActivityManagerNative mBroadcastsScheduled = true; } - public Intent registerReceiver(IApplicationThread caller, + public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission) { synchronized(this) { ProcessRecord callerApp = null; @@ -10957,6 +10986,13 @@ public final class ActivityManagerService extends ActivityManagerNative + " (pid=" + Binder.getCallingPid() + ") when registering receiver " + receiver); } + if (callerApp.info.uid != Process.SYSTEM_UID && + !callerApp.pkgList.contains(callerPackage)) { + throw new SecurityException("Given caller package " + callerPackage + + " is not running in process " + callerApp); + } + } else { + callerPackage = null; } List allSticky = null; @@ -11001,7 +11037,7 @@ public final class ActivityManagerService extends ActivityManagerNative } mRegisteredReceivers.put(receiver.asBinder(), rl); } - BroadcastFilter bf = new BroadcastFilter(filter, rl, permission); + BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission); rl.add(bf); if (!bf.debugCheck()) { Slog.w(TAG, "==> For Dynamic broadast"); @@ -12155,6 +12191,7 @@ public final class ActivityManagerService extends ActivityManagerNative info.activityInfo.applicationInfo.uid); if (app != null && app.thread != null) { try { + app.addPackage(info.activityInfo.packageName); processCurBroadcastLocked(r, app); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index b94ee587793c..b1da69f90326 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -652,6 +652,7 @@ final class ActivityStack { if (app != null && app.thread != null) { try { + app.addPackage(r.info.packageName); realStartActivityLocked(r, app, andResume, checkConfig); return; } catch (RemoteException e) { diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java index 2e784d30caa7..b49bc220ac03 100644 --- a/services/java/com/android/server/am/BroadcastFilter.java +++ b/services/java/com/android/server/am/BroadcastFilter.java @@ -25,12 +25,14 @@ import java.io.PrintWriter; class BroadcastFilter extends IntentFilter { // Back-pointer to the list this filter is in. final ReceiverList receiverList; + final String packageName; final String requiredPermission; BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList, - String _requiredPermission) { + String _packageName, String _requiredPermission) { super(_filter); receiverList = _receiverList; + packageName = _packageName; requiredPermission = _requiredPermission; } diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index ee6e4204af39..8ed0cc11abd8 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -177,13 +177,13 @@ class PendingIntentRecord extends IIntentSender.Stub { } public int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver) { + IIntentReceiver finishedReceiver, String requiredPermission) { return sendInner(code, intent, resolvedType, finishedReceiver, - null, null, 0, 0, 0); + requiredPermission, null, null, 0, 0, 0); } int sendInner(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, + IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues) { synchronized(owner) { @@ -246,8 +246,8 @@ class PendingIntentRecord extends IIntentSender.Stub { // that the broadcast be delivered synchronously owner.broadcastIntentInPackage(key.packageName, uid, finalIntent, resolvedType, - finishedReceiver, code, null, null, null, - (finishedReceiver != null), false); + finishedReceiver, code, null, null, + requiredPermission, (finishedReceiver != null), false); sendFinish = false; } catch (RuntimeException e) { Slog.w(ActivityManagerService.TAG, diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1c57bc1a5476..685613efef09 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1799,10 +1799,10 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = hw_h - (hw_h * v); const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y; - vtx[2] = x; vtx[3] = y + h; - vtx[4] = x + w; vtx[5] = y + h; - vtx[6] = x + w; vtx[7] = y; + vtx[0] = x; vtx[1] = y + h; + vtx[2] = x; vtx[3] = y; + vtx[4] = x + w; vtx[5] = y; + vtx[6] = x + w; vtx[7] = y + h; } }; @@ -1817,10 +1817,10 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() const GLfloat h = 1.0f; const GLfloat x = (hw_w - w) * 0.5f; const GLfloat y = (hw_h - h) * 0.5f; - vtx[0] = x; vtx[1] = y; - vtx[2] = x; vtx[3] = y + h; - vtx[4] = x + w; vtx[5] = y + h; - vtx[6] = x + w; vtx[7] = y; + vtx[0] = x; vtx[1] = y + h; + vtx[2] = x; vtx[3] = y; + vtx[4] = x + w; vtx[5] = y; + vtx[6] = x + w; vtx[7] = y + h; } }; diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 5fc0bf9b51e2..67f515888f22 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -556,6 +556,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (DBG) log("onDataConnectionAttached: start polling notify attached"); startNetStatPoll(); notifyDataConnection(Phone.REASON_DATA_ATTACHED); + } else { + // update APN availability so that APN can be enabled. + notifyDataAvailability(Phone.REASON_DATA_ATTACHED); } setupDataOnReadyApns(Phone.REASON_DATA_ATTACHED); @@ -1528,13 +1531,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { createAllApnList(); if (mRadioAvailable) { if (DBG) log("onRecordsLoaded: notifying data availability"); - notifyDataAvailability(null); + notifyDataAvailability(Phone.REASON_SIM_LOADED); } setupDataOnReadyApns(Phone.REASON_SIM_LOADED); } @Override protected void onSetDependencyMet(String apnType, boolean met) { + // don't allow users to tweak hipri to work around default dependency not met + if (Phone.APN_TYPE_HIPRI.equals(apnType)) return; + ApnContext apnContext = mApnContexts.get(apnType); if (apnContext == null) { loge("onSetDependencyMet: ApnContext not found in onSetDependencyMet(" + @@ -1542,6 +1548,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return; } applyNewState(apnContext, apnContext.isEnabled(), met); + if (Phone.APN_TYPE_DEFAULT.equals(apnType)) { + // tie actions on default to similar actions on HIPRI regarding dependencyMet + apnContext = mApnContexts.get(Phone.APN_TYPE_HIPRI); + if (apnContext != null) applyNewState(apnContext, apnContext.isEnabled(), met); + } } private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) { @@ -1627,11 +1638,17 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { @Override protected void onRoamingOff() { if (DBG) log("onRoamingOff"); + // Notify data availability so APN can be enabled. + notifyDataAvailability(Phone.REASON_ROAMING_OFF); + setupDataOnReadyApns(Phone.REASON_ROAMING_OFF); } @Override protected void onRoamingOn() { + // Notify data availability so APN can be enabled. + notifyDataAvailability(Phone.REASON_ROAMING_ON); + if (getDataOnRoamingEnabled()) { if (DBG) log("onRoamingOn: setup data on roaming"); setupDataOnReadyApns(Phone.REASON_ROAMING_ON); diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java index 76780c02b3df..00d47ac63ce6 100644 --- a/voip/java/com/android/server/sip/SipWakeupTimer.java +++ b/voip/java/com/android/server/sip/SipWakeupTimer.java @@ -83,7 +83,7 @@ class SipWakeupTimer extends BroadcastReceiver { mEventQueue = null; } - private synchronized boolean stopped() { + private boolean stopped() { if (mEventQueue == null) { Log.w(TAG, "Timer stopped"); return true; @@ -233,7 +233,7 @@ class SipWakeupTimer extends BroadcastReceiver { } @Override - public void onReceive(Context context, Intent intent) { + public synchronized void onReceive(Context context, Intent intent) { // This callback is already protected by AlarmManager's wake lock. String action = intent.getAction(); if (getAction().equals(action) @@ -261,7 +261,7 @@ class SipWakeupTimer extends BroadcastReceiver { } } - private synchronized void execute(long triggerTime) { + private void execute(long triggerTime) { if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " + showTime(triggerTime) + ": " + mEventQueue.size()); if (stopped() || mEventQueue.isEmpty()) return; |