diff options
Diffstat (limited to 'core/java')
38 files changed, 800 insertions, 549 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 3da35d3..4e340c0 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -259,13 +259,51 @@ public abstract class AccessibilityService extends Service { public static final int GESTURE_COUNTER_CLOCKWISE_CIRCLE = 10; /** + * The user has performed a left and up gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_LEFT_AND_UP = 11; + + /** + * The user has performed a left and down gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_LEFT_AND_DOWN = 12; + + /** + * The user has performed a right and up gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_RIGHT_AND_UP = 13; + + /** + * The user has performed a right and down gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_RIGHT_AND_DOWN = 14; + + /** + * The user has performed an up and left gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_UP_AND_LEFT = 15; + + /** + * The user has performed an up and right gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_UP_AND_RIGHT = 16; + + /** + * The user has performed an down and left gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_DOWN_AND_LEFT = 17; + + /** + * The user has performed an down and right gesture on the touch screen. + */ + public static final int GESTURE_SWIPE_DOWN_AND_RIGHT = 18; + + /** * The {@link Intent} that must be declared as handled by the service. */ public static final String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService"; - private static final int UNDEFINED = -1; - /** * Name under which an AccessibilityService component publishes information * about itself. This meta-data must reference an XML resource containing an @@ -284,6 +322,28 @@ public abstract class AccessibilityService extends Service { */ public static final String SERVICE_META_DATA = "android.accessibilityservice"; + /** + * Action to go back. + */ + public static final int GLOBAL_ACTION_BACK = 1; + + /** + * Action to go home. + */ + public static final int GLOBAL_ACTION_HOME = 2; + + /** + * Action to open the recents. + */ + public static final int GLOBAL_ACTION_RECENTS = 3; + + /** + * Action to open the notifications. + */ + public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; + + private static final int UNDEFINED = -1; + private static final String LOG_TAG = "AccessibilityService"; interface Callbacks { @@ -344,23 +404,39 @@ public abstract class AccessibilityService extends Service { protected void onGesture(int gestureId) { // TODO: Describe the default gesture processing in the javaDoc once it is finalized. + // Global actions. + switch (gestureId) { + case GESTURE_SWIPE_DOWN_AND_LEFT: { + performGlobalAction(GLOBAL_ACTION_BACK); + } return; + case GESTURE_SWIPE_DOWN_AND_RIGHT: { + performGlobalAction(GLOBAL_ACTION_HOME); + } return; + case GESTURE_SWIPE_UP_AND_LEFT: { + performGlobalAction(GLOBAL_ACTION_RECENTS); + } return; + case GESTURE_SWIPE_UP_AND_RIGHT: { + performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS); + } return; + } + // Cache the id to avoid locking final int connectionId = mConnectionId; if (connectionId == UNDEFINED) { throw new IllegalStateException("AccessibilityService not connected." + " Did you receive a call of onServiceConnected()?"); } - AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance() - .findAccessibilityNodeInfoByAccessibilityId(connectionId, - AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, - AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); + AccessibilityNodeInfo root = getRootInActiveWindow(); if (root == null) { return; } - AccessibilityNodeInfo current = root.findFocus(View.FOCUS_ACCESSIBILITY); + + AccessibilityNodeInfo current = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); if (current == null) { current = root; } + + // Local actions. AccessibilityNodeInfo next = null; switch (gestureId) { case GESTURE_SWIPE_UP: { @@ -402,6 +478,46 @@ public abstract class AccessibilityService extends Service { } /** + * Gets the root node in the currently active window if this service + * can retrieve window content. + * + * @return The root node if this service can retrieve window content. + */ + public AccessibilityNodeInfo getRootInActiveWindow() { + return AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, + AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); + } + + /** + * Performs a global action. Such an action can be performed + * at any moment regardless of the current application or user + * location in that application. For example going back, going + * home, opening recents, etc. + * + * @param action The action to perform. + * @return Whether the action was successfully performed. + * + * @see #GLOBAL_ACTION_BACK + * @see #GLOBAL_ACTION_HOME + * @see #GLOBAL_ACTION_NOTIFICATIONS + * @see #GLOBAL_ACTION_RECENTS + */ + public final boolean performGlobalAction(int action) { + IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + if (connection != null) { + try { + return connection.perfromGlobalAction(action); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling performGlobalAction", re); + } + } + return false; + } + + /** * Gets the an {@link AccessibilityServiceInfo} describing this * {@link AccessibilityService}. This method is useful if one wants * to change some of the dynamically configurable properties at diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 30da9db..1bd5387 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -160,4 +160,12 @@ interface IAccessibilityServiceConnection { * @return The associated accessibility service info. */ AccessibilityServiceInfo getServiceInfo(); + + /** + * Performs a global action, such as going home, going back, etc. + * + * @param action The action to perform. + * @return Whether the action was performed. + */ + boolean perfromGlobalAction(int action); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 1c820dc..35bc7ff 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -549,7 +549,7 @@ import java.util.HashMap; * super.onCreate(savedInstanceState); * * SharedPreferences mPrefs = getSharedPreferences(); - * mCurViewMode = mPrefs.getInt("view_mode" DAY_VIEW_MODE); + * mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE); * } * * protected void onPause() { diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4d5238c..6f95e26 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -34,5 +34,8 @@ interface INotificationManager void cancelToast(String pkg, ITransientNotification callback); void enqueueNotificationWithTag(String pkg, String tag, int id, in Notification notification, inout int[] idReceived); void cancelNotificationWithTag(String pkg, String tag, int id); + + void setNotificationsEnabledForPackage(String pkg, boolean enabled); + boolean areNotificationsEnabledForPackage(String pkg); } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 22d84f0..b581f99 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -190,12 +190,6 @@ public class Notification implements Parcelable public RemoteViews contentView; /** - * The view that will represent this notification in the pop-up "intruder alert" dialog. - * @hide - */ - public RemoteViews intruderView; - - /** * A large-format version of {@link #contentView}, giving the Notification an * opportunity to show more detail. The system UI may choose to show this * instead of the normal content view at its discretion. @@ -590,9 +584,6 @@ public class Notification implements Parcelable actions = parcel.createTypedArray(Action.CREATOR); if (parcel.readInt() != 0) { - intruderView = RemoteViews.CREATOR.createFromParcel(parcel); - } - if (parcel.readInt() != 0) { bigContentView = RemoteViews.CREATOR.createFromParcel(parcel); } } @@ -658,9 +649,6 @@ public class Notification implements Parcelable for(int i=0; i<this.actions.length; i++) { that.actions[i] = this.actions[i].clone(); } - if (this.intruderView != null) { - that.intruderView = this.intruderView.clone(); - } if (this.bigContentView != null) { that.bigContentView = this.bigContentView.clone(); } @@ -755,13 +743,6 @@ public class Notification implements Parcelable parcel.writeTypedArray(actions, 0); - if (intruderView != null) { - parcel.writeInt(1); - intruderView.writeToParcel(parcel, 0); - } else { - parcel.writeInt(0); - } - if (bigContentView != null) { parcel.writeInt(1); bigContentView.writeToParcel(parcel, 0); @@ -942,8 +923,6 @@ public class Notification implements Parcelable private Bundle mExtras; private int mPriority; private ArrayList<Action> mActions = new ArrayList<Action>(3); - private boolean mCanHasIntruder; - private boolean mIntruderActionsShowText; private boolean mUseChronometer; /** @@ -1349,38 +1328,6 @@ public class Notification implements Parcelable return this; } - /** - * Specify whether this notification should pop up as an - * "intruder alert" (a small window that shares the screen with the - * current activity). This sort of notification is (as the name implies) - * very intrusive, so use it sparingly for notifications that require - * the user's attention. - * - * Notes: - * <ul> - * <li>Intruder alerts only show when the screen is on.</li> - * <li>Intruder alerts take precedence over fullScreenIntents.</li> - * </ul> - * - * @param intrude Whether to pop up an intruder alert (default false). - */ - public Builder setUsesIntruderAlert(boolean intrude) { - mCanHasIntruder = intrude; - return this; - } - - /** - * Control text on intruder alert action buttons. By default, action - * buttons in intruders do not show textual labels. - * - * @param showActionText Whether to show text labels beneath action - * icons (default false). - */ - public Builder setIntruderActionsShowText(boolean showActionText) { - mIntruderActionsShowText = showActionText; - return this; - } - private void setFlag(int mask, boolean value) { if (value) { mFlags |= mask; @@ -1506,45 +1453,6 @@ public class Notification implements Parcelable return applyStandardTemplateWithActions(R.layout.notification_template_base); } - private RemoteViews makeIntruderView(boolean showLabels) { - RemoteViews intruderView = new RemoteViews(mContext.getPackageName(), - R.layout.notification_intruder_content); - if (mLargeIcon != null) { - intruderView.setImageViewBitmap(R.id.icon, mLargeIcon); - intruderView.setViewVisibility(R.id.icon, View.VISIBLE); - } else if (mSmallIcon != 0) { - intruderView.setImageViewResource(R.id.icon, mSmallIcon); - intruderView.setViewVisibility(R.id.icon, View.VISIBLE); - } else { - intruderView.setViewVisibility(R.id.icon, View.GONE); - } - if (mContentTitle != null) { - intruderView.setTextViewText(R.id.title, mContentTitle); - } - if (mContentText != null) { - intruderView.setTextViewText(R.id.text, mContentText); - } - if (mActions.size() > 0) { - intruderView.setViewVisibility(R.id.actions, View.VISIBLE); - int N = mActions.size(); - if (N>3) N=3; - final int[] BUTTONS = { R.id.action0, R.id.action1, R.id.action2 }; - for (int i=0; i<N; i++) { - final Action action = mActions.get(i); - final int buttonId = BUTTONS[i]; - - intruderView.setViewVisibility(buttonId, View.VISIBLE); - intruderView.setTextViewText(buttonId, showLabels ? action.title : null); - intruderView.setTextViewCompoundDrawables(buttonId, 0, action.icon, 0, 0); - intruderView.setContentDescription(buttonId, action.title); - intruderView.setOnClickPendingIntent(buttonId, action.actionIntent); - } - } else { - intruderView.setViewVisibility(R.id.actions, View.GONE); - } - return intruderView; - } - private RemoteViews generateActionButton(Action action) { RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action); button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0); @@ -1579,9 +1487,6 @@ public class Notification implements Parcelable n.ledOffMS = mLedOffMs; n.defaults = mDefaults; n.flags = mFlags; - if (mCanHasIntruder) { - n.intruderView = makeIntruderView(mIntruderActionsShowText); - } n.bigContentView = makeBigContentView(); if (mLedOnMs != 0 && mLedOffMs != 0) { n.flags |= FLAG_SHOW_LIGHTS; diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index 800d0d2..dfd1820 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -77,7 +77,20 @@ public class ClipboardManager extends android.text.ClipboardManager { } }; + /** + * Defines a listener callback that is invoked when the primary clip on the clipboard changes. + * Objects that want to register a listener call + * {@link android.content.ClipboardManager#addPrimaryClipChangedListener(OnPrimaryClipChangedListener) + * addPrimaryClipChangedListener()} with an + * object that implements OnPrimaryClipChangedListener. + * + */ public interface OnPrimaryClipChangedListener { + + /** + * Callback that is invoked by {@link android.content.ClipboardManager} when the primary + * clip changes. + */ void onPrimaryClipChanged(); } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 04f6377..acdc488 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -390,6 +390,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen // Called by SQLiteConnectionPool only. void reconfigure(SQLiteDatabaseConfiguration configuration) { + mOnlyAllowReadOnlyOperations = false; + // Register custom functions. final int functionCount = configuration.customFunctions.size(); for (int i = 0; i < functionCount; i++) { diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 640b47b..4fb710e 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -234,48 +234,6 @@ public class Camera { }; /** - * Creates a new Camera object to access a particular hardware camera. - * - * <p>When <code>force</code> is set to false, this will throw an exception - * if the same camera is already opened by other clients. If true, the other - * client will be disconnected from the camera they opened. If the device - * can only support one camera running at a time, all camera-using clients - * will be disconnected from their cameras. - * - * <p>A camera being held by an application can be taken away by other - * applications at any time. Before the camera is taken, applications will - * get {@link #CAMERA_ERROR_RELEASED} and have some time to clean up. Apps - * receiving this callback must immediately stop video recording and then - * call {@link #release()} on their camera object. Otherwise, it will be - * released by the frameworks in a short time. After receiving - * CAMERA_ERROR_RELEASED, apps should not call any method except <code> - * release</code> and {@link #isReleased()}. After a camera is taken away, - * all methods will throw exceptions except <code>isReleased</code> and - * <code>release</code>. Apps can use <code>isReleased</code> to see if the - * camera has been taken away. If the camera is taken away, the apps can - * silently finish themselves or show a dialog. - * - * <p>Applications with android.permission.KEEP_CAMERA can request to keep - * the camera. That is, the camera will not be taken by other applications - * while it is opened. The permission can only be obtained by trusted - * platform applications, such as those implementing lock screen security - * features. - * - * @param cameraId the hardware camera to access, between 0 and - * {@link #getNumberOfCameras()}-1. - * @param force true to take the ownership from the existing client if the - * camera has been opened by other clients. - * @param keep true if the applications do not want other apps to take the - * camera. Only the apps with android.permission.KEEP_CAMERA can keep - * the camera. - * @return a new Camera object, connected, locked and ready for use. - * @hide - */ - public static Camera open(int cameraId, boolean force, boolean keep) { - return new Camera(cameraId, force, keep); - } - - /** * Creates a new Camera object to access a particular hardware camera. If * the same camera is opened by other applications, this will throw a * RuntimeException. @@ -305,7 +263,7 @@ public class Camera { * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName) */ public static Camera open(int cameraId) { - return new Camera(cameraId, false, false); + return new Camera(cameraId); } /** @@ -320,13 +278,13 @@ public class Camera { for (int i = 0; i < numberOfCameras; i++) { getCameraInfo(i, cameraInfo); if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { - return new Camera(i, false, false); + return new Camera(i); } } return null; } - Camera(int cameraId, boolean force, boolean keep) { + Camera(int cameraId) { mShutterCallback = null; mRawImageCallback = null; mJpegCallback = null; @@ -343,7 +301,7 @@ public class Camera { mEventHandler = null; } - native_setup(new WeakReference<Camera>(this), cameraId, force, keep); + native_setup(new WeakReference<Camera>(this), cameraId); } /** @@ -356,8 +314,7 @@ public class Camera { release(); } - private native final void native_setup(Object camera_this, int cameraId, - boolean force, boolean keep); + private native final void native_setup(Object camera_this, int cameraId); private native final void native_release(); @@ -372,18 +329,6 @@ public class Camera { } /** - * Whether the camera is released. When any camera method throws an - * exception, applications can use this to check whether the camera has been - * taken by other clients. If true, it means other clients have taken the - * camera. The applications can silently finish themselves or show a dialog. - * - * @return whether the camera is released. - * @see #open(int, boolean, boolean) - * @hide - */ - public native final boolean isReleased(); - - /** * Unlocks the camera to allow another process to access it. * Normally, the camera is locked to the process with an active Camera * object until {@link #release()} is called. To allow rapid handoff @@ -1377,17 +1322,6 @@ public class Camera { public static final int CAMERA_ERROR_UNKNOWN = 1; /** - * Camera was released because another client has opened the camera. The - * application should call {@link #release()} after getting this. The apps - * should not call any method except <code>release</code> and {@link #isReleased()} - * after this. - * - * @see Camera.ErrorCallback - * @hide - */ - public static final int CAMERA_ERROR_RELEASED = 2; - - /** * Media server died. In this case, the application must release the * Camera object and instantiate a new one. * @see Camera.ErrorCallback diff --git a/core/java/android/net/DhcpInfoInternal.java b/core/java/android/net/DhcpInfoInternal.java index fa77bc5..7ab8047 100644 --- a/core/java/android/net/DhcpInfoInternal.java +++ b/core/java/android/net/DhcpInfoInternal.java @@ -19,9 +19,8 @@ package android.net; import android.text.TextUtils; import android.util.Log; -import java.net.InetAddress; import java.net.Inet4Address; -import java.net.UnknownHostException; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -42,6 +41,11 @@ public class DhcpInfoInternal { public String serverAddress; public int leaseDuration; + /** + * Vendor specific information (from RFC 2132). + */ + public String vendorInfo; + private Collection<RouteInfo> mRoutes; public DhcpInfoInternal() { diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl index 077a675..3361a7b 100644 --- a/core/java/android/net/nsd/INsdManager.aidl +++ b/core/java/android/net/nsd/INsdManager.aidl @@ -26,4 +26,5 @@ import android.os.Messenger; interface INsdManager { Messenger getMessenger(); + void setEnabled(boolean enable); } diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index dac8d20..77e97e1 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -16,6 +16,8 @@ package android.net.nsd; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; import android.os.IBinder; @@ -133,6 +135,40 @@ public class NsdManager { private static final String TAG = "NsdManager"; INsdManager mService; + /** + * Broadcast intent action to indicate whether network service discovery is + * enabled or disabled. An extra {@link #EXTRA_NSD_STATE} provides the state + * information as int. + * + * @see #EXTRA_NSD_STATE + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_NSD_STATE_CHANGED = + "android.net.nsd.STATE_CHANGED"; + + /** + * The lookup key for an int that indicates whether network service discovery is enabled + * or disabled. Retrieve it with {@link android.content.Intent#getIntExtra(String,int)}. + * + * @see #NSD_STATE_DISABLED + * @see #NSD_STATE_ENABLED + */ + public static final String EXTRA_NSD_STATE = "nsd_state"; + + /** + * Network service discovery is disabled + * + * @see #ACTION_NSD_STATE_CHANGED + */ + public static final int NSD_STATE_DISABLED = 1; + + /** + * Network service discovery is enabled + * + * @see #ACTION_NSD_STATE_CHANGED + */ + public static final int NSD_STATE_ENABLED = 2; + private static final int BASE = Protocol.BASE_NSD_MANAGER; /** @hide */ @@ -188,6 +224,12 @@ public class NsdManager { /** @hide */ public static final int STOP_RESOLVE_SUCCEEDED = BASE + 23; + /** @hide */ + public static final int ENABLE = BASE + 24; + /** @hide */ + public static final int DISABLE = BASE + 25; + + /** * Create a new Nsd instance. Applications use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve @@ -312,8 +354,8 @@ public class NsdManager { private DnsSdResolveListener mDnsSdResolveListener; private ActionListener mDnsSdStopResolveListener; - AsyncChannel mAsyncChannel; - ServiceHandler mHandler; + private AsyncChannel mAsyncChannel; + private ServiceHandler mHandler; class ServiceHandler extends Handler { ServiceHandler(Looper looper) { super(looper); @@ -594,6 +636,13 @@ public class NsdManager { c.mAsyncChannel.sendMessage(STOP_RESOLVE); } + /** Internal use only @hide */ + public void setEnabled(boolean enabled) { + try { + mService.setEnabled(enabled); + } catch (RemoteException e) { } + } + /** * Get a reference to NetworkService handler. This is used to establish * an AsyncChannel communication with the service diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index c4aa691..f6a6da6 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -19,6 +19,7 @@ package android.provider; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.app.Activity; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.ContentProviderClient; @@ -98,6 +99,32 @@ public final class CalendarContract { public static final String ACTION_EVENT_REMINDER = "android.intent.action.EVENT_REMINDER"; /** + * Activity Action: Display the event to the user in the custom app as + * specified in {@link EventsColumns#CUSTOM_APP_PACKAGE}. The custom app + * will be started via {@link Activity#startActivityForResult(Intent, int)} + * and it should call {@link Activity#setResult(int)} with + * {@link Activity#RESULT_OK} or {@link Activity#RESULT_CANCELED} to + * acknowledge whether the action was handled or not. + * <p> + * Input: {@link Intent#getData} has the event URI. The extra + * {@link #EXTRA_EVENT_BEGIN_TIME} has the start time of the instance. The + * extra {@link #EXTRA_CUSTOM_APP_URI} will have the + * {@link EventsColumns#CUSTOM_APP_URI}. + * <p> + * Output: {@link Activity#RESULT_OK} if this was handled; otherwise + * {@link Activity#RESULT_CANCELED} + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_HANDLE_CUSTOM_EVENT = + "android.provider.calendar.action.HANDLE_CUSTOM_EVENT"; + + /** + * Intent Extras key: {@link EventsColumns#CUSTOM_APP_URI} for the event in + * the {@link #ACTION_HANDLE_CUSTOM_EVENT} intent + */ + public static final String EXTRA_CUSTOM_APP_URI = "eventUri"; + + /** * Intent Extras key: The start time of an event or an instance of a * recurring event. (milliseconds since epoch) */ @@ -1176,6 +1203,22 @@ public final class CalendarContract { * <P>Type: INTEGER (boolean, readonly)</P> */ public static final String CAN_INVITE_OTHERS = "canInviteOthers"; + + /** + * The package name of the custom app that can provide a richer + * experience for the event. See the ACTION TYPE + * {@link CalendarContract#ACTION_HANDLE_CUSTOM_EVENT} for details. + * Column name. + * <P> Type: TEXT </P> + */ + public static final String CUSTOM_APP_PACKAGE = "customAppPackage"; + + /** + * The URI used by the custom app for the event. Column name. + * <P>Type: TEXT</P> + */ + public static final String CUSTOM_APP_URI = "customAppUri"; + } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 38945c2..79d0144 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -306,13 +306,11 @@ public final class MediaStore { /** * The width of the image/video in pixels. - * @hide */ public static final String WIDTH = "width"; /** * The height of the image/video in pixels. - * @hide */ public static final String HEIGHT = "height"; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6dfbb2f..3a5fdd1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3277,6 +3277,12 @@ public final class Settings { "wifi_mobile_data_transition_wakelock_timeout_ms"; /** + * Whether network service discovery is enabled. + * @hide + */ + public static final String NSD_ON = "nsd_on"; + + /** * Whether background data usage is allowed by the user. See * ConnectivityManager for more info. */ diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 7ce96c0..6917fb2 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -140,6 +140,7 @@ public abstract class WallpaperService extends Service { boolean mIsCreating; boolean mDrawingAllowed; boolean mOffsetsChanged; + boolean mFixedSizeAllowed; int mWidth; int mHeight; int mFormat; @@ -211,7 +212,7 @@ public abstract class WallpaperService extends Service { @Override public void setFixedSize(int width, int height) { - if (Process.myUid() != Process.SYSTEM_UID) { + if (!mFixedSizeAllowed) { // Regular apps can't do this. It can only work for // certain designs of window animations, so you can't // rely on it. @@ -385,7 +386,12 @@ public abstract class WallpaperService extends Service { updateSurface(false, false, false); } } - + + /** {@hide} */ + public void setFixedSizeAllowed(boolean allowed) { + mFixedSizeAllowed = allowed; + } + /** * Called once to initialize the engine. After returning, the * engine's surface will be created by the framework. diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index 6c1a6bf..54c62ee 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -52,13 +52,16 @@ final class AccessibilityInteractionController { private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList = new ArrayList<AccessibilityNodeInfo>(); - private final Handler mHandler = new PrivateHandler(); + private final Handler mHandler; private final ViewRootImpl mViewRootImpl; private final AccessibilityNodePrefetcher mPrefetcher; public AccessibilityInteractionController(ViewRootImpl viewRootImpl) { + // mView is never null - the caller has already checked. + Looper looper = viewRootImpl.mView.mContext.getMainLooper(); + mHandler = new PrivateHandler(looper); mViewRootImpl = viewRootImpl; mPrefetcher = new AccessibilityNodePrefetcher(); } @@ -846,8 +849,8 @@ final class AccessibilityInteractionController { private final static int MSG_FIND_FOCUS = 5; private final static int MSG_FOCUS_SEARCH = 6; - public PrivateHandler() { - super(Looper.getMainLooper()); + public PrivateHandler(Looper looper) { + super(looper); } @Override diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index ad2283e..bda8016 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -143,6 +143,49 @@ public class Display { } /** + * Return the range of display sizes an application can expect to encounter + * under normal operation, as long as there is no physical change in screen + * size. This is basically the sizes you will see as the orientation + * changes, taking into account whatever screen decoration there is in + * each rotation. For example, the status bar is always at the top of the + * screen, so it will reduce the height both in landscape and portrait, and + * the smallest height returned here will be the smaller of the two. + * + * This is intended for applications to get an idea of the range of sizes + * they will encounter while going through device rotations, to provide a + * stable UI through rotation. The sizes here take into account all standard + * system decorations that reduce the size actually available to the + * application: the status bar, navigation bar, system bar, etc. It does + * <em>not</em> take into account more transient elements like an IME + * soft keyboard. + * + * @param outSmallestSize Filled in with the smallest width and height + * that the application will encounter, in pixels (not dp units). The x + * (width) dimension here directly corresponds to + * {@link android.content.res.Configuration#smallestScreenWidthDp + * Configuration.smallestScreenWidthDp}, except the value here is in raw + * screen pixels rather than dp units. Your application may of course + * still get smaller space yet if, for example, a soft keyboard is + * being displayed. + * @param outLargestSize Filled in with the largest width and height + * that the application will encounter, in pixels (not dp units). Your + * application may of course still get larger space than this if, + * for example, screen decorations like the status bar are being hidden. + */ + public void getCurrentSizeRange(Point outSmallestSize, Point outLargestSize) { + try { + IWindowManager wm = getWindowManager(); + wm.getCurrentSizeRange(outSmallestSize, outLargestSize); + } catch (RemoteException e) { + Slog.w("Display", "Unable to get display size range", e); + outSmallestSize.x = 0; + outSmallestSize.y = 0; + outLargestSize.x = 0; + outLargestSize.y = 0; + } + } + + /** * Return the maximum screen size dimension that will happen. This is * mostly for wallpapers. * @hide diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index b70d7b5..e1f01db 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -58,6 +58,7 @@ interface IWindowManager void getDisplaySize(out Point size); void getRealDisplaySize(out Point size); int getMaximumSizeDimension(); + void getCurrentSizeRange(out Point smallestSize, out Point largestSize); void setForcedDisplaySize(int longDimen, int shortDimen); void clearForcedDisplaySize(); diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index 4848a7a..85f435c 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -167,6 +167,19 @@ public final class InputDevice implements Parcelable { /** * The input source is a stylus pointing device. + * <p> + * Note that this bit merely indicates that an input device is capable of obtaining + * input from a stylus. To determine whether a given touch event was produced + * by a stylus, examine the tool type returned by {@link MotionEvent#getToolType(int)} + * for each individual pointer. + * </p><p> + * A single touch event may multiple pointers with different tool types, + * such as an event that has one pointer with tool type + * {@link MotionEvent#TOOL_TYPE_FINGER} and another pointer with tool type + * {@link MotionEvent#TOOL_TYPE_STYLUS}. So it is important to examine + * the tool type of each pointer, regardless of the source reported + * by {@link MotionEvent#getSource()}. + * </p> * * @see #SOURCE_CLASS_POINTER */ diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 12d7b12..1c61c6c 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -24,6 +24,7 @@ import android.util.SparseIntArray; import android.hardware.input.InputManager; import java.lang.Character; +import java.text.Normalizer; /** * Describes the keys provided by a keyboard device and their associated labels. @@ -135,6 +136,106 @@ public class KeyCharacterMap implements Parcelable { */ public static final int MODIFIER_BEHAVIOR_CHORDED_OR_TOGGLED = 1; + /* + * This bit will be set in the return value of {@link #get(int, int)} if the + * key is a "dead key." + */ + public static final int COMBINING_ACCENT = 0x80000000; + + /** + * Mask the return value from {@link #get(int, int)} with this value to get + * a printable representation of the accent character of a "dead key." + */ + public static final int COMBINING_ACCENT_MASK = 0x7FFFFFFF; + + /* Characters used to display placeholders for dead keys. */ + private static final int ACCENT_ACUTE = '\u00B4'; + private static final int ACCENT_BREVE = '\u02D8'; + private static final int ACCENT_CARON = '\u02C7'; + private static final int ACCENT_CEDILLA = '\u00B8'; + private static final int ACCENT_COMMA_ABOVE = '\u1FBD'; + private static final int ACCENT_COMMA_ABOVE_RIGHT = '\u02BC'; + private static final int ACCENT_DOT_ABOVE = '\u02D9'; + private static final int ACCENT_DOUBLE_ACUTE = '\u02DD'; + private static final int ACCENT_GRAVE = '\u02CB'; + private static final int ACCENT_CIRCUMFLEX = '\u02C6'; + private static final int ACCENT_MACRON = '\u00AF'; + private static final int ACCENT_MACRON_BELOW = '\u02CD'; + private static final int ACCENT_OGONEK = '\u02DB'; + private static final int ACCENT_REVERSED_COMMA_ABOVE = '\u02BD'; + private static final int ACCENT_RING_ABOVE = '\u02DA'; + private static final int ACCENT_TILDE = '\u02DC'; + private static final int ACCENT_TURNED_COMMA_ABOVE = '\u02BB'; + private static final int ACCENT_UMLAUT = '\u00A8'; + + /* Legacy dead key display characters used in previous versions of the API. + * We still support these characters by mapping them to their non-legacy version. */ + private static final int ACCENT_GRAVE_LEGACY = '`'; + private static final int ACCENT_CIRCUMFLEX_LEGACY = '^'; + private static final int ACCENT_TILDE_LEGACY = '~'; + + /** + * Maps Unicode combining diacritical to display-form dead key. + */ + private static final SparseIntArray sCombiningToAccent = new SparseIntArray(); + private static final SparseIntArray sAccentToCombining = new SparseIntArray(); + static { + addCombining('\u0300', ACCENT_GRAVE); + addCombining('\u0301', ACCENT_ACUTE); + addCombining('\u0302', ACCENT_CIRCUMFLEX); + addCombining('\u0303', ACCENT_TILDE); + addCombining('\u0304', ACCENT_MACRON); + addCombining('\u0306', ACCENT_BREVE); + addCombining('\u0307', ACCENT_DOT_ABOVE); + addCombining('\u0308', ACCENT_UMLAUT); + //addCombining('\u0309', ACCENT_HOOK_ABOVE); + addCombining('\u030A', ACCENT_RING_ABOVE); + addCombining('\u030B', ACCENT_DOUBLE_ACUTE); + addCombining('\u030C', ACCENT_CARON); + //addCombining('\u030D', ACCENT_VERTICAL_LINE_ABOVE); + //addCombining('\u030E', ACCENT_DOUBLE_VERTICAL_LINE_ABOVE); + //addCombining('\u030F', ACCENT_DOUBLE_GRAVE); + //addCombining('\u0310', ACCENT_CANDRABINDU); + //addCombining('\u0311', ACCENT_INVERTED_BREVE); + addCombining('\u0312', ACCENT_TURNED_COMMA_ABOVE); + addCombining('\u0313', ACCENT_COMMA_ABOVE); + addCombining('\u0314', ACCENT_REVERSED_COMMA_ABOVE); + addCombining('\u0315', ACCENT_COMMA_ABOVE_RIGHT); + //addCombining('\u031B', ACCENT_HORN); + //addCombining('\u0323', ACCENT_DOT_BELOW); + //addCombining('\u0326', ACCENT_COMMA_BELOW); + addCombining('\u0327', ACCENT_CEDILLA); + addCombining('\u0328', ACCENT_OGONEK); + //addCombining('\u0329', ACCENT_VERTICAL_LINE_BELOW); + addCombining('\u0331', ACCENT_MACRON_BELOW); + //addCombining('\u0342', ACCENT_PERISPOMENI); + //addCombining('\u0344', ACCENT_DIALYTIKA_TONOS); + //addCombining('\u0345', ACCENT_YPOGEGRAMMENI); + + // One-way mappings to equivalent preferred accents. + sCombiningToAccent.append('\u0340', ACCENT_GRAVE); + sCombiningToAccent.append('\u0341', ACCENT_ACUTE); + sCombiningToAccent.append('\u0343', ACCENT_COMMA_ABOVE); + + // One-way legacy mappings to preserve compatibility with older applications. + sAccentToCombining.append(ACCENT_GRAVE_LEGACY, '\u0300'); + sAccentToCombining.append(ACCENT_CIRCUMFLEX_LEGACY, '\u0302'); + sAccentToCombining.append(ACCENT_TILDE_LEGACY, '\u0303'); + } + + private static void addCombining(int combining, int accent) { + sCombiningToAccent.append(combining, accent); + sAccentToCombining.append(accent, combining); + } + + /** + * Maps combinations of (display-form) combining key and second character + * to combined output character. + * These mappings are derived from the Unicode NFC tables as needed. + */ + private static final SparseIntArray sDeadKeyCache = new SparseIntArray(); + private static final StringBuilder sDeadKeyBuilder = new StringBuilder(); + public static final Parcelable.Creator<KeyCharacterMap> CREATOR = new Parcelable.Creator<KeyCharacterMap>() { public KeyCharacterMap createFromParcel(Parcel in) { @@ -230,9 +331,9 @@ public class KeyCharacterMap implements Parcelable { metaState = KeyEvent.normalizeMetaState(metaState); char ch = nativeGetCharacter(mPtr, keyCode, metaState); - int map = COMBINING.get(ch); + int map = sCombiningToAccent.get(ch); if (map != 0) { - return map; + return map | COMBINING_ACCENT; } else { return ch; } @@ -346,7 +447,25 @@ public class KeyCharacterMap implements Parcelable { * @return The combined character, or 0 if the characters cannot be combined. */ public static int getDeadChar(int accent, int c) { - return DEAD.get((accent << 16) | c); + int combining = sAccentToCombining.get(accent); + if (combining == 0) { + return 0; + } + + final int combination = (combining << 16) | c; + int combined; + synchronized (sDeadKeyCache) { + combined = sDeadKeyCache.get(combination, -1); + if (combined == -1) { + sDeadKeyBuilder.setLength(0); + sDeadKeyBuilder.append((char)c); + sDeadKeyBuilder.append((char)combining); + String result = Normalizer.normalize(sDeadKeyBuilder, Normalizer.Form.NFC); + combined = result.length() == 1 ? result.charAt(0) : 0; + sDeadKeyCache.put(combination, combined); + } + } + return combined; } /** @@ -560,159 +679,6 @@ public class KeyCharacterMap implements Parcelable { } /** - * Maps Unicode combining diacritical to display-form dead key - * (display character shifted left 16 bits). - */ - private static SparseIntArray COMBINING = new SparseIntArray(); - - /** - * Maps combinations of (display-form) dead key and second character - * to combined output character. - */ - private static SparseIntArray DEAD = new SparseIntArray(); - - /* - * TODO: Change the table format to support full 21-bit-wide - * accent characters and combined characters if ever necessary. - */ - private static final int ACUTE = '\u00B4' << 16; - private static final int GRAVE = '`' << 16; - private static final int CIRCUMFLEX = '^' << 16; - private static final int TILDE = '~' << 16; - private static final int UMLAUT = '\u00A8' << 16; - - /* - * This bit will be set in the return value of {@link #get(int, int)} if the - * key is a "dead key." - */ - public static final int COMBINING_ACCENT = 0x80000000; - /** - * Mask the return value from {@link #get(int, int)} with this value to get - * a printable representation of the accent character of a "dead key." - */ - public static final int COMBINING_ACCENT_MASK = 0x7FFFFFFF; - - static { - COMBINING.put('\u0300', (GRAVE >> 16) | COMBINING_ACCENT); - COMBINING.put('\u0301', (ACUTE >> 16) | COMBINING_ACCENT); - COMBINING.put('\u0302', (CIRCUMFLEX >> 16) | COMBINING_ACCENT); - COMBINING.put('\u0303', (TILDE >> 16) | COMBINING_ACCENT); - COMBINING.put('\u0308', (UMLAUT >> 16) | COMBINING_ACCENT); - - DEAD.put(ACUTE | 'A', '\u00C1'); - DEAD.put(ACUTE | 'C', '\u0106'); - DEAD.put(ACUTE | 'E', '\u00C9'); - DEAD.put(ACUTE | 'G', '\u01F4'); - DEAD.put(ACUTE | 'I', '\u00CD'); - DEAD.put(ACUTE | 'K', '\u1E30'); - DEAD.put(ACUTE | 'L', '\u0139'); - DEAD.put(ACUTE | 'M', '\u1E3E'); - DEAD.put(ACUTE | 'N', '\u0143'); - DEAD.put(ACUTE | 'O', '\u00D3'); - DEAD.put(ACUTE | 'P', '\u1E54'); - DEAD.put(ACUTE | 'R', '\u0154'); - DEAD.put(ACUTE | 'S', '\u015A'); - DEAD.put(ACUTE | 'U', '\u00DA'); - DEAD.put(ACUTE | 'W', '\u1E82'); - DEAD.put(ACUTE | 'Y', '\u00DD'); - DEAD.put(ACUTE | 'Z', '\u0179'); - DEAD.put(ACUTE | 'a', '\u00E1'); - DEAD.put(ACUTE | 'c', '\u0107'); - DEAD.put(ACUTE | 'e', '\u00E9'); - DEAD.put(ACUTE | 'g', '\u01F5'); - DEAD.put(ACUTE | 'i', '\u00ED'); - DEAD.put(ACUTE | 'k', '\u1E31'); - DEAD.put(ACUTE | 'l', '\u013A'); - DEAD.put(ACUTE | 'm', '\u1E3F'); - DEAD.put(ACUTE | 'n', '\u0144'); - DEAD.put(ACUTE | 'o', '\u00F3'); - DEAD.put(ACUTE | 'p', '\u1E55'); - DEAD.put(ACUTE | 'r', '\u0155'); - DEAD.put(ACUTE | 's', '\u015B'); - DEAD.put(ACUTE | 'u', '\u00FA'); - DEAD.put(ACUTE | 'w', '\u1E83'); - DEAD.put(ACUTE | 'y', '\u00FD'); - DEAD.put(ACUTE | 'z', '\u017A'); - DEAD.put(CIRCUMFLEX | 'A', '\u00C2'); - DEAD.put(CIRCUMFLEX | 'C', '\u0108'); - DEAD.put(CIRCUMFLEX | 'E', '\u00CA'); - DEAD.put(CIRCUMFLEX | 'G', '\u011C'); - DEAD.put(CIRCUMFLEX | 'H', '\u0124'); - DEAD.put(CIRCUMFLEX | 'I', '\u00CE'); - DEAD.put(CIRCUMFLEX | 'J', '\u0134'); - DEAD.put(CIRCUMFLEX | 'O', '\u00D4'); - DEAD.put(CIRCUMFLEX | 'S', '\u015C'); - DEAD.put(CIRCUMFLEX | 'U', '\u00DB'); - DEAD.put(CIRCUMFLEX | 'W', '\u0174'); - DEAD.put(CIRCUMFLEX | 'Y', '\u0176'); - DEAD.put(CIRCUMFLEX | 'Z', '\u1E90'); - DEAD.put(CIRCUMFLEX | 'a', '\u00E2'); - DEAD.put(CIRCUMFLEX | 'c', '\u0109'); - DEAD.put(CIRCUMFLEX | 'e', '\u00EA'); - DEAD.put(CIRCUMFLEX | 'g', '\u011D'); - DEAD.put(CIRCUMFLEX | 'h', '\u0125'); - DEAD.put(CIRCUMFLEX | 'i', '\u00EE'); - DEAD.put(CIRCUMFLEX | 'j', '\u0135'); - DEAD.put(CIRCUMFLEX | 'o', '\u00F4'); - DEAD.put(CIRCUMFLEX | 's', '\u015D'); - DEAD.put(CIRCUMFLEX | 'u', '\u00FB'); - DEAD.put(CIRCUMFLEX | 'w', '\u0175'); - DEAD.put(CIRCUMFLEX | 'y', '\u0177'); - DEAD.put(CIRCUMFLEX | 'z', '\u1E91'); - DEAD.put(GRAVE | 'A', '\u00C0'); - DEAD.put(GRAVE | 'E', '\u00C8'); - DEAD.put(GRAVE | 'I', '\u00CC'); - DEAD.put(GRAVE | 'N', '\u01F8'); - DEAD.put(GRAVE | 'O', '\u00D2'); - DEAD.put(GRAVE | 'U', '\u00D9'); - DEAD.put(GRAVE | 'W', '\u1E80'); - DEAD.put(GRAVE | 'Y', '\u1EF2'); - DEAD.put(GRAVE | 'a', '\u00E0'); - DEAD.put(GRAVE | 'e', '\u00E8'); - DEAD.put(GRAVE | 'i', '\u00EC'); - DEAD.put(GRAVE | 'n', '\u01F9'); - DEAD.put(GRAVE | 'o', '\u00F2'); - DEAD.put(GRAVE | 'u', '\u00F9'); - DEAD.put(GRAVE | 'w', '\u1E81'); - DEAD.put(GRAVE | 'y', '\u1EF3'); - DEAD.put(TILDE | 'A', '\u00C3'); - DEAD.put(TILDE | 'E', '\u1EBC'); - DEAD.put(TILDE | 'I', '\u0128'); - DEAD.put(TILDE | 'N', '\u00D1'); - DEAD.put(TILDE | 'O', '\u00D5'); - DEAD.put(TILDE | 'U', '\u0168'); - DEAD.put(TILDE | 'V', '\u1E7C'); - DEAD.put(TILDE | 'Y', '\u1EF8'); - DEAD.put(TILDE | 'a', '\u00E3'); - DEAD.put(TILDE | 'e', '\u1EBD'); - DEAD.put(TILDE | 'i', '\u0129'); - DEAD.put(TILDE | 'n', '\u00F1'); - DEAD.put(TILDE | 'o', '\u00F5'); - DEAD.put(TILDE | 'u', '\u0169'); - DEAD.put(TILDE | 'v', '\u1E7D'); - DEAD.put(TILDE | 'y', '\u1EF9'); - DEAD.put(UMLAUT | 'A', '\u00C4'); - DEAD.put(UMLAUT | 'E', '\u00CB'); - DEAD.put(UMLAUT | 'H', '\u1E26'); - DEAD.put(UMLAUT | 'I', '\u00CF'); - DEAD.put(UMLAUT | 'O', '\u00D6'); - DEAD.put(UMLAUT | 'U', '\u00DC'); - DEAD.put(UMLAUT | 'W', '\u1E84'); - DEAD.put(UMLAUT | 'X', '\u1E8C'); - DEAD.put(UMLAUT | 'Y', '\u0178'); - DEAD.put(UMLAUT | 'a', '\u00E4'); - DEAD.put(UMLAUT | 'e', '\u00EB'); - DEAD.put(UMLAUT | 'h', '\u1E27'); - DEAD.put(UMLAUT | 'i', '\u00EF'); - DEAD.put(UMLAUT | 'o', '\u00F6'); - DEAD.put(UMLAUT | 't', '\u1E97'); - DEAD.put(UMLAUT | 'u', '\u00FC'); - DEAD.put(UMLAUT | 'w', '\u1E85'); - DEAD.put(UMLAUT | 'x', '\u1E8D'); - DEAD.put(UMLAUT | 'y', '\u00FF'); - } - - /** * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded. */ public static class UnavailableException extends AndroidRuntimeException { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0ded5f9..ab3413e 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2472,6 +2472,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal int mSystemUiVisibility; /** + * Reference count for transient state. + * @see #setHasTransientState(boolean) + */ + int mTransientStateCount = 0; + + /** * Count of how many windows this view has been attached to. */ int mWindowAttachCount; @@ -5400,21 +5406,32 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal /** * Set whether this view is currently tracking transient state that the - * framework should attempt to preserve when possible. + * framework should attempt to preserve when possible. This flag is reference counted, + * so every call to setHasTransientState(true) should be paired with a later call + * to setHasTransientState(false). * * @param hasTransientState true if this view has transient state */ public void setHasTransientState(boolean hasTransientState) { - if (hasTransientState() == hasTransientState) return; - - mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) | - (hasTransientState ? HAS_TRANSIENT_STATE : 0); - if (mParent != null) { - try { - mParent.childHasTransientStateChanged(this, hasTransientState); - } catch (AbstractMethodError e) { - Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + - " does not fully implement ViewParent", e); + mTransientStateCount = hasTransientState ? mTransientStateCount + 1 : + mTransientStateCount - 1; + if (mTransientStateCount < 0) { + mTransientStateCount = 0; + Log.e(VIEW_LOG_TAG, "hasTransientState decremented below 0: " + + "unmatched pair of setHasTransientState calls"); + } + if ((hasTransientState && mTransientStateCount == 1) || + (hasTransientState && mTransientStateCount == 0)) { + // update flag if we've just incremented up from 0 or decremented down to 0 + mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) | + (hasTransientState ? HAS_TRANSIENT_STATE : 0); + if (mParent != null) { + try { + mParent.childHasTransientStateChanged(this, hasTransientState); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } } } } @@ -6359,16 +6376,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public boolean performAccessibilityAction(int action) { switch (action) { case AccessibilityNodeInfo.ACTION_CLICK: { - final long now = SystemClock.uptimeMillis(); - // Send down. - MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, - getWidth() / 2, getHeight() / 2, 0); - onTouchEvent(event); - // Send up. - event.setAction(MotionEvent.ACTION_UP); - onTouchEvent(event); - // Clean up. - event.recycle(); + if (isClickable()) { + performClick(); + } + } break; + case AccessibilityNodeInfo.ACTION_LONG_CLICK: { + if (isLongClickable()) { + performLongClick(); + } } break; case AccessibilityNodeInfo.ACTION_FOCUS: { if (!hasFocus()) { @@ -7323,7 +7338,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT); // If the window does not have input focus we take away accessibility // focus as soon as the user stop hovering over the view. - if (!mAttachInfo.mHasWindowFocus) { + if (mAttachInfo != null && !mAttachInfo.mHasWindowFocus) { getViewRootImpl().setAccessibilityFocusedHost(null); } } @@ -12299,7 +12314,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal final int flags = parent.mGroupFlags; final boolean initialized = a.isInitialized(); if (!initialized) { - a.initialize(mRight - mLeft, mBottom - mTop, getWidth(), getHeight()); + a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight()); a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop); onAnimationStart(); } @@ -13846,13 +13861,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal */ public Insets getLayoutInsets() { if (mLayoutInsets == null) { - if (mBackground == null) { - mLayoutInsets = Insets.NONE; - } else { - Rect insetRect = new Rect(); - boolean hasInsets = mBackground.getLayoutInsets(insetRect); - mLayoutInsets = hasInsets ? Insets.of(insetRect) : Insets.NONE; - } + mLayoutInsets = (mBackground == null) ? Insets.NONE : mBackground.getLayoutInsets(); } return mLayoutInsets; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6371963..1641d4c 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1569,6 +1569,43 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return handled; } + private void exitHoverTargets() { + if (mHoveredSelf || mFirstHoverTarget != null) { + final long now = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + dispatchHoverEvent(event); + event.recycle(); + } + } + + private void cancelHoverTarget(View view) { + HoverTarget predecessor = null; + HoverTarget target = mFirstHoverTarget; + while (target != null) { + final HoverTarget next = target.next; + if (target.child == view) { + if (predecessor == null) { + mFirstHoverTarget = next; + } else { + predecessor.next = next; + } + target.recycle(); + + final long now = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + view.dispatchHoverEvent(event); + event.recycle(); + return; + } + predecessor = target; + target = next; + } + } + /** @hide */ @Override protected boolean hasHoveredChild() { @@ -1997,6 +2034,32 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } } + private void cancelTouchTarget(View view) { + TouchTarget predecessor = null; + TouchTarget target = mFirstTouchTarget; + while (target != null) { + final TouchTarget next = target.next; + if (target.child == view) { + if (predecessor == null) { + mFirstTouchTarget = next; + } else { + predecessor.next = next; + } + target.recycle(); + + final long now = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(now, now, + MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + view.dispatchTouchEvent(event); + event.recycle(); + return; + } + predecessor = target; + target = next; + } + } + /** * Returns true if a child view can receive pointer events. * @hide @@ -2416,6 +2479,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // first send it an ACTION_CANCEL motion event. cancelAndClearTouchTargets(null); + // Similarly, set ACTION_EXIT to all hover targets and clear them. + exitHoverTargets(); + // In case view is detached while transition is running mLayoutSuppressed = false; @@ -3453,6 +3519,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = true; } + cancelTouchTarget(view); + cancelHoverTarget(view); + if (view.getAnimation() != null || (mTransitioningViews != null && mTransitioningViews.contains(view))) { addDisappearingView(view); @@ -3533,6 +3602,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = view; } + cancelTouchTarget(view); + cancelHoverTarget(view); + if (view.getAnimation() != null || (mTransitioningViews != null && mTransitioningViews.contains(view))) { addDisappearingView(view); @@ -3603,6 +3675,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager clearChildFocus = view; } + cancelTouchTarget(view); + cancelHoverTarget(view); + if (view.getAnimation() != null || (mTransitioningViews != null && mTransitioningViews.contains(view))) { addDisappearingView(view); @@ -3648,6 +3723,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.clearFocus(); } + cancelTouchTarget(child); + cancelHoverTarget(child); + if ((animate && child.getAnimation() != null) || (mTransitioningViews != null && mTransitioningViews.contains(child))) { addDisappearingView(child); @@ -5792,11 +5870,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager throw new IllegalStateException("Instance already recycled."); } clear(); - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - mIsPooled = true; - sPool = this; - sPoolSize++; + synchronized (sPoolLock) { + if (sPoolSize < MAX_POOL_SIZE) { + mNext = sPool; + mIsPooled = true; + sPool = this; + sPoolSize++; + } } } @@ -5889,11 +5969,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager throw new IllegalStateException("Instance already recycled."); } clear(); - if (sPoolSize < MAX_POOL_SIZE) { - mNext = sPool; - mIsPooled = true; - sPool = this; - sPoolSize++; + synchronized (sPoolLock) { + if (sPoolSize < MAX_POOL_SIZE) { + mNext = sPool; + mIsPooled = true; + sPool = this; + sPoolSize++; + } } } @@ -5943,9 +6025,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (widthDiference != 0) { return -widthDiference; } - // Return nondeterministically one of them since we do - // not want to ignore any views. - return 1; + // Just break the tie somehow. The accessibliity ids are unique + // and stable, hence this is deterministic tie breaking. + return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId(); } private void init(ViewGroup root, View view) { diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 3626aba..e573056 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -714,6 +714,7 @@ public class ViewPropertyAnimator { * value accordingly. */ private void startAnimation() { + mView.setHasTransientState(true); ValueAnimator animator = ValueAnimator.ofFloat(1.0f); ArrayList<NameValuesHolder> nameValueList = (ArrayList<NameValuesHolder>) mPendingAnimations.clone(); @@ -960,6 +961,7 @@ public class ViewPropertyAnimator { @Override public void onAnimationEnd(Animator animation) { + mView.setHasTransientState(false); if (mListener != null) { mListener.onAnimationEnd(animation); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1472993..247f673 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1199,13 +1199,13 @@ public final class ViewRootImpl implements ViewParent, } } + // Execute enqueued actions on every traversal in case a detached view enqueued an action + getRunQueue().executeActions(attachInfo.mHandler); + boolean insetsChanged = false; boolean layoutRequested = mLayoutRequested && !mStopped; if (layoutRequested) { - // Execute enqueued actions on every layout in case a view that was detached - // enqueued an action after being detached - getRunQueue().executeActions(attachInfo.mHandler); final Resources res = mView.getContext().getResources(); @@ -2665,6 +2665,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_PROCESS_INPUT_EVENTS = 19; private final static int MSG_DISPATCH_SCREEN_STATE = 20; private final static int MSG_INVALIDATE_DISPLAY_LIST = 21; + private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 22; final class ViewRootHandler extends Handler { @Override @@ -2712,6 +2713,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_DISPATCH_SCREEN_STATE"; case MSG_INVALIDATE_DISPLAY_LIST: return "MSG_INVALIDATE_DISPLAY_LIST"; + case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: + return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST"; } return super.getMessageName(message); } @@ -2921,6 +2924,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_INVALIDATE_DISPLAY_LIST: { invalidateDisplayLists(); } break; + case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: { + setAccessibilityFocusedHost(null); + } break; } } } @@ -5066,7 +5072,7 @@ public final class ViewRootImpl implements ViewParent, } } else { ensureNoConnection(); - setAccessibilityFocusedHost(null); + mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget(); } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 1071c65..c5f2062 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -84,7 +84,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action that gives input focus to the node. */ - public static final int ACTION_FOCUS = 0x00000001; + public static final int ACTION_FOCUS = 0x00000001; /** * Action that clears input focus of the node. @@ -102,19 +102,24 @@ public class AccessibilityNodeInfo implements Parcelable { public static final int ACTION_CLEAR_SELECTION = 0x00000008; /** - * Action that gives accessibility focus to the node. + * Action that clicks on the node info. */ - public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000010; + public static final int ACTION_CLICK = 0x00000010; /** - * Action that clears accessibility focus of the node. + * Action that clicks on the node. + */ + public static final int ACTION_LONG_CLICK = 0x00000020; + + /** + * Action that gives accessibility focus to the node. */ - public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000020; + public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040; /** - * Action that clicks on the node info./AccessibilityNodeInfoCache.java + * Action that clears accessibility focus of the node. */ - public static final int ACTION_CLICK = 0x00000040; + public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080; /** * The input focus. @@ -278,9 +283,9 @@ public class AccessibilityNodeInfo implements Parcelable { (root != null) ? root.getAccessibilityViewId() : UNDEFINED; mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId); } - + /** - * Find the view that has the input focus. The search starts from + * Find the view that has the specified focus type. The search starts from * the view represented by this node info. * * @param focus The focus to find. One of {@link #FOCUS_INPUT} or diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 893849b..c8cfb0a 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -1074,7 +1074,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc private static final int STD_SPEED = 480; // pixels per second // time for the longest scroll animation private static final int MAX_DURATION = 750; // milliseconds - private static final int SLIDE_TITLE_DURATION = 500; // milliseconds // Used by OverScrollGlow OverScroller mScroller; @@ -1486,8 +1485,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc switch (eventType) { case WebViewInputDispatcher.EVENT_TYPE_LONG_PRESS: HitTestResult hitTest = getHitTestResult(); - if (hitTest != null - && hitTest.getType() != HitTestResult.UNKNOWN_TYPE) { + if (hitTest != null) { performLongClick(); } break; @@ -4013,17 +4011,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc * @param url The URL of the page which has finished loading. */ /* package */ void onPageFinished(String url) { - if (mPageThatNeedsToSlideTitleBarOffScreen != null) { - // If the user is now on a different page, or has scrolled the page - // past the point where the title bar is offscreen, ignore the - // scroll request. - if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url) - && getScrollX() == 0 && getScrollY() == 0) { - pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true, - SLIDE_TITLE_DURATION); - } - mPageThatNeedsToSlideTitleBarOffScreen = null; - } mZoomManager.onPageFinished(url); injectAccessibilityForUrl(url); } @@ -4135,93 +4122,16 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc return -1; } - /** - * The URL of a page that sent a message to scroll the title bar off screen. - * - * Many mobile sites tell the page to scroll to (0,1) in order to scroll the - * title bar off the screen. Sometimes, the scroll position is set before - * the page finishes loading. Rather than scrolling while the page is still - * loading, keep track of the URL and new scroll position so we can perform - * the scroll once the page finishes loading. - */ - private String mPageThatNeedsToSlideTitleBarOffScreen; - - /** - * The destination Y scroll position to be used when the page finishes - * loading. See mPageThatNeedsToSlideTitleBarOffScreen. - */ - private int mYDistanceToSlideTitleOffScreen; - - // scale from content to view coordinates, and pin - // return true if pin caused the final x/y different than the request cx/cy, - // and a future scroll may reach the request cx/cy after our size has - // changed - // return false if the view scroll to the exact position as it is requested, - // where negative numbers are taken to mean 0 - private boolean setContentScrollTo(int cx, int cy) { - if (mDrawHistory) { - // disallow WebView to change the scroll position as History Picture - // is used in the view system. - // One known case where this is called is that WebCore tries to - // restore the scroll position. As history Picture already uses the - // saved scroll position, it is ok to skip this. - return false; - } - int vx; - int vy; - if ((cx | cy) == 0) { - // If the page is being scrolled to (0,0), do not add in the title - // bar's height, and simply scroll to (0,0). (The only other work - // in contentToView_ is to multiply, so this would not change 0.) - vx = 0; - vy = 0; - } else { - vx = contentToViewX(cx); - vy = contentToViewY(cy); - } -// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" + -// vx + " " + vy + "]"); - // Some mobile sites attempt to scroll the title bar off the page by - // scrolling to (0,1). If we are at the top left corner of the - // page, assume this is an attempt to scroll off the title bar, and - // animate the title bar off screen slowly enough that the user can see - // it. - if (cx == 0 && cy == 1 && getScrollX() == 0 && getScrollY() == 0 - && getTitleHeight() > 0) { - // FIXME: 100 should be defined somewhere as our max progress. - if (getProgress() < 100) { - // Wait to scroll the title bar off screen until the page has - // finished loading. Keep track of the URL and the destination - // Y position - mPageThatNeedsToSlideTitleBarOffScreen = getUrl(); - mYDistanceToSlideTitleOffScreen = vy; - } else { - pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION); - } - // Since we are animating, we have not yet reached the desired - // scroll position. Do not return true to request another attempt - return false; - } - pinScrollTo(vx, vy, false, 0); - // If the request was to scroll to a negative coordinate, treat it as if - // it was a request to scroll to 0 - if ((getScrollX() != vx && cx >= 0) || (getScrollY() != vy && cy >= 0)) { - return true; - } else { - return false; - } - } - // scale from content to view coordinates, and pin - private void spawnContentScrollTo(int cx, int cy) { + private void contentScrollTo(int cx, int cy, boolean animate) { if (mDrawHistory) { // disallow WebView to change the scroll position as History Picture // is used in the view system. return; } - int vx = contentToViewDimension(cx - mScrollOffset.x); - int vy = contentToViewDimension(cy - mScrollOffset.y); - pinScrollBy(vx, vy, true, 0); + int vx = contentToViewX(cx); + int vy = contentToViewY(cy); + pinScrollTo(vx, vy, animate, 0); } /** @@ -7428,11 +7338,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc } } final Point p = (Point) msg.obj; - if (msg.arg1 == 1) { - spawnContentScrollTo(p.x, p.y); - } else { - setContentScrollTo(p.x, p.y); - } + contentScrollTo(p.x, p.y, msg.arg1 == 1); break; } case UPDATE_ZOOM_RANGE: { @@ -8073,7 +7979,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc int scrollX = viewState.mShouldStartScrolledRight ? getContentWidth() : viewState.mScrollX; int scrollY = viewState.mScrollY; - setContentScrollTo(scrollX, scrollY); + contentScrollTo(scrollX, scrollY, false); if (!mDrawHistory) { // As we are on a new page, hide the keyboard hideSoftKeyboard(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index aea23c0..f86262e 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -46,6 +46,8 @@ import junit.framework.Assert; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; import java.util.Map; import java.util.Set; @@ -1182,7 +1184,7 @@ public final class WebViewCore { private Handler mHandler; // Message queue for containing messages before the WebCore thread is // ready. - private ArrayList<Message> mMessages = new ArrayList<Message>(); + private LinkedList<Message> mMessages = new LinkedList<Message>(); // Flag for blocking messages. This is used during DESTROY to avoid // posting more messages to the EventHub or to WebView's event handler. private boolean mBlockMessages; @@ -1822,10 +1824,13 @@ public final class WebViewCore { mDrawIsScheduled = false; } if (mMessages != null) { - Throwable throwable = new Throwable( - "EventHub.removeMessages(int what = " + what + ") is not supported " + - "before the WebViewCore is set up."); - Log.w(LOGTAG, Log.getStackTraceString(throwable)); + Iterator<Message> iter = mMessages.iterator(); + while (iter.hasNext()) { + Message m = iter.next(); + if (m.what == what) { + iter.remove(); + } + } } else { mHandler.removeMessages(what); } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index ca5648a..ae68794 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -123,7 +123,7 @@ public abstract class AbsSeekBar extends ProgressBar { invalidate(); if (needUpdate) { updateThumbPos(getWidth(), getHeight()); - if (thumb.isStateful()) { + if (thumb != null && thumb.isStateful()) { // Note that if the states are different this won't work. // For now, let's consider that an app bug. int[] state = getDrawableState(); diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java index 2ff6b70..bda64ba 100644 --- a/core/java/android/widget/ExpandableListConnector.java +++ b/core/java/android/widget/ExpandableListConnector.java @@ -372,7 +372,8 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { @Override public boolean isEnabled(int flatListPos) { - final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position; + final PositionMetadata metadata = getUnflattenedPos(flatListPos); + final ExpandableListPosition pos = metadata.position; boolean retValue; if (pos.type == ExpandableListPosition.CHILD) { @@ -382,7 +383,7 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { retValue = true; } - pos.recycle(); + metadata.recycle(); return retValue; } @@ -461,7 +462,8 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { @Override public int getItemViewType(int flatListPos) { - final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position; + final PositionMetadata metadata = getUnflattenedPos(flatListPos); + final ExpandableListPosition pos = metadata.position; int retValue; if (mExpandableListAdapter instanceof HeterogeneousExpandableList) { @@ -481,7 +483,7 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { } } - pos.recycle(); + metadata.recycle(); return retValue; } @@ -590,8 +592,10 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { * @param groupPos position of the group to collapse */ boolean collapseGroup(int groupPos) { - PositionMetadata pm = getFlattenedPos(ExpandableListPosition.obtain( - ExpandableListPosition.GROUP, groupPos, -1, -1)); + ExpandableListPosition elGroupPos = ExpandableListPosition.obtain( + ExpandableListPosition.GROUP, groupPos, -1, -1); + PositionMetadata pm = getFlattenedPos(elGroupPos); + elGroupPos.recycle(); if (pm == null) return false; boolean retValue = collapseGroup(pm); @@ -631,8 +635,10 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { * @param groupPos the group to be expanded */ boolean expandGroup(int groupPos) { - PositionMetadata pm = getFlattenedPos(ExpandableListPosition.obtain( - ExpandableListPosition.GROUP, groupPos, -1, -1)); + ExpandableListPosition elGroupPos = ExpandableListPosition.obtain( + ExpandableListPosition.GROUP, groupPos, -1, -1); + PositionMetadata pm = getFlattenedPos(elGroupPos); + elGroupPos.recycle(); boolean retValue = expandGroup(pm); pm.recycle(); return retValue; @@ -971,7 +977,10 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { public int groupInsertIndex; private void resetState() { - position = null; + if (position != null) { + position.recycle(); + position = null; + } groupMetadata = null; groupInsertIndex = 0; } @@ -1005,6 +1014,7 @@ class ExpandableListConnector extends BaseAdapter implements Filterable { } public void recycle() { + resetState(); synchronized (sPool) { if (sPool.size() < MAX_POOL_SIZE) { sPool.add(this); diff --git a/core/java/android/widget/ExpandableListPosition.java b/core/java/android/widget/ExpandableListPosition.java index e8d6113..bb68da6 100644 --- a/core/java/android/widget/ExpandableListPosition.java +++ b/core/java/android/widget/ExpandableListPosition.java @@ -125,6 +125,10 @@ class ExpandableListPosition { return elp; } + /** + * Do not call this unless you obtained this via ExpandableListPosition.obtain(). + * PositionMetadata will handle recycling its own children. + */ public void recycle() { synchronized (sPool) { if (sPool.size() < MAX_POOL_SIZE) { diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java index c2d8bda..a746370 100644 --- a/core/java/android/widget/ExpandableListView.java +++ b/core/java/android/widget/ExpandableListView.java @@ -326,7 +326,6 @@ public class ExpandableListView extends ListView { indicator.draw(canvas); } } - pos.recycle(); } @@ -613,8 +612,10 @@ public class ExpandableListView extends ListView { * was already expanded, this will return false) */ public boolean expandGroup(int groupPos, boolean animate) { - PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition.obtain( - ExpandableListPosition.GROUP, groupPos, -1, -1)); + ExpandableListPosition elGroupPos = ExpandableListPosition.obtain( + ExpandableListPosition.GROUP, groupPos, -1, -1); + PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos); + elGroupPos.recycle(); boolean retValue = mConnector.expandGroup(pm); if (mOnGroupExpandListener != null) { @@ -776,8 +777,10 @@ public class ExpandableListView extends ListView { * @return The flat list position for the given child or group. */ public int getFlatListPosition(long packedPosition) { - PositionMetadata pm = mConnector.getFlattenedPos(ExpandableListPosition - .obtainPosition(packedPosition)); + ExpandableListPosition elPackedPos = ExpandableListPosition + .obtainPosition(packedPosition); + PositionMetadata pm = mConnector.getFlattenedPos(elPackedPos); + elPackedPos.recycle(); final int flatListPosition = pm.position.flatListPos; pm.recycle(); return getAbsoluteFlatPosition(flatListPosition); @@ -988,11 +991,11 @@ public class ExpandableListView extends ListView { final int adjustedPosition = getFlatPositionForConnector(flatListPosition); PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition); ExpandableListPosition pos = pm.position; - pm.recycle(); id = getChildOrGroupId(pos); long packedPosition = pos.getPackedPosition(); - pos.recycle(); + + pm.recycle(); return new ExpandableListContextMenuInfo(view, packedPosition, id); } diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 0786909..471f259 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -29,6 +29,8 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; +import android.text.method.AllCapsTransformationMethod; +import android.text.method.TransformationMethod2; import android.util.AttributeSet; import android.view.Gravity; import android.view.MotionEvent; @@ -91,6 +93,7 @@ public class Switch extends CompoundButton { private ColorStateList mTextColors; private Layout mOnLayout; private Layout mOffLayout; + private TransformationMethod2 mSwitchTransformationMethod; @SuppressWarnings("hiding") private final Rect mTempRect = new Rect(); @@ -207,6 +210,15 @@ public class Switch extends CompoundButton { setSwitchTypefaceByIndex(typefaceIndex, styleIndex); + boolean allCaps = appearance.getBoolean(com.android.internal.R.styleable. + TextAppearance_textAllCaps, false); + if (allCaps) { + mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext()); + mSwitchTransformationMethod.setLengthChangesAllowed(true); + } else { + mSwitchTransformationMethod = null; + } + appearance.recycle(); } @@ -526,8 +538,12 @@ public class Switch extends CompoundButton { } private Layout makeLayout(CharSequence text) { - return new StaticLayout(text, mTextPaint, - (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)), + final CharSequence transformed = (mSwitchTransformationMethod != null) + ? mSwitchTransformationMethod.getTransformation(text, this) + : text; + + return new StaticLayout(transformed, mTextPaint, + (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)), Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true); } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 1ba6d43..d0071e3 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -16,6 +16,7 @@ package com.android.internal.app; +import com.android.internal.view.ActionBarPolicy; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPopupHelper; import com.android.internal.view.menu.SubMenuBuilder; @@ -183,18 +184,13 @@ public class ActionBarImpl extends ActionBar { mContextDisplayMode = mActionView.isSplitActionBar() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; - // Older apps get the home button interaction enabled by default. - // Newer apps need to enable it explicitly. - setHomeButtonEnabled(mContext.getApplicationInfo().targetSdkVersion < - Build.VERSION_CODES.ICE_CREAM_SANDWICH); - - setHasEmbeddedTabs(mContext.getResources().getBoolean( - com.android.internal.R.bool.action_bar_embed_tabs)); + ActionBarPolicy abp = ActionBarPolicy.get(mContext); + setHomeButtonEnabled(abp.enableHomeButtonByDefault()); + setHasEmbeddedTabs(abp.hasEmbeddedTabs()); } public void onConfigurationChanged(Configuration newConfig) { - setHasEmbeddedTabs(mContext.getResources().getBoolean( - com.android.internal.R.bool.action_bar_embed_tabs)); + setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs()); } private void setHasEmbeddedTabs(boolean hasEmbeddedTabs) { diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 0c5d5ef..d1c2d2e 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -150,7 +150,7 @@ public class AsyncChannel { */ public static final int CMD_CHANNEL_DISCONNECTED = BASE + 4; - private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED + 1; + private static final int CMD_TO_STRING_COUNT = CMD_CHANNEL_DISCONNECTED - BASE + 1; private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT]; static { sCmdToString[CMD_CHANNEL_HALF_CONNECTED - BASE] = "CMD_CHANNEL_HALF_CONNECTED"; diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java new file mode 100644 index 0000000..0c6b780 --- /dev/null +++ b/core/java/com/android/internal/view/ActionBarPolicy.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.view; + +import com.android.internal.R; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.os.Build; +import android.view.ViewConfiguration; + +/** + * Allows components to query for various configuration policy decisions + * about how the action bar should lay out and behave on the current device. + */ +public class ActionBarPolicy { + private Context mContext; + + public static ActionBarPolicy get(Context context) { + return new ActionBarPolicy(context); + } + + private ActionBarPolicy(Context context) { + mContext = context; + } + + public int getMaxActionButtons() { + return mContext.getResources().getInteger(R.integer.max_action_buttons); + } + + public boolean showsOverflowMenuButton() { + return !ViewConfiguration.get(mContext).hasPermanentMenuKey(); + } + + public int getEmbeddedMenuWidthLimit() { + return mContext.getResources().getDisplayMetrics().widthPixels / 2; + } + + public boolean hasEmbeddedTabs() { + final int targetSdk = mContext.getApplicationInfo().targetSdkVersion; + if (targetSdk >= Build.VERSION_CODES.JELLY_BEAN) { + return mContext.getResources().getBoolean(R.bool.action_bar_embed_tabs); + } + + // The embedded tabs policy changed in Jellybean; give older apps the old policy + // so they get what they expect. + return mContext.getResources().getBoolean(R.bool.action_bar_embed_tabs_pre_jb); + } + + public int getTabContainerHeight() { + TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar, + com.android.internal.R.attr.actionBarStyle, 0); + int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0); + Resources r = mContext.getResources(); + if (!hasEmbeddedTabs()) { + // Stacked tabs; limit the height + height = Math.min(height, + r.getDimensionPixelSize(R.dimen.action_bar_stacked_max_height)); + } + a.recycle(); + return height; + } + + public boolean enableHomeButtonByDefault() { + // Older apps get the home button interaction enabled by default. + // Newer apps need to enable it explicitly. + return mContext.getApplicationInfo().targetSdkVersion < + Build.VERSION_CODES.ICE_CREAM_SANDWICH; + } + + public int getStackedTabMaxWidth() { + return mContext.getResources().getDimensionPixelSize( + R.dimen.action_bar_stacked_tab_max_width); + } +} diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index dca45a9..73324c0 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -16,6 +16,7 @@ package com.android.internal.view.menu; +import com.android.internal.view.ActionBarPolicy; import com.android.internal.view.menu.ActionMenuView.ActionMenuChildView; import android.content.Context; @@ -29,7 +30,6 @@ 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; @@ -79,17 +79,18 @@ public class ActionMenuPresenter extends BaseMenuPresenter final Resources res = context.getResources(); + final ActionBarPolicy abp = ActionBarPolicy.get(context); if (!mReserveOverflowSet) { - mReserveOverflow = !ViewConfiguration.get(context).hasPermanentMenuKey(); + mReserveOverflow = abp.showsOverflowMenuButton(); } if (!mWidthLimitSet) { - mWidthLimit = res.getDisplayMetrics().widthPixels / 2; + mWidthLimit = abp.getEmbeddedMenuWidthLimit(); } // Measure for initial configuration if (!mMaxItemsSet) { - mMaxItems = res.getInteger(com.android.internal.R.integer.max_action_buttons); + mMaxItems = abp.getMaxActionButtons(); } int width = mWidthLimit; diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java index 06f5158..25a9c54 100644 --- a/core/java/com/android/internal/widget/AbsActionBarView.java +++ b/core/java/com/android/internal/widget/AbsActionBarView.java @@ -161,10 +161,12 @@ public abstract class AbsActionBarView extends ViewGroup { @Override public void setVisibility(int visibility) { - if (mVisibilityAnim != null) { - mVisibilityAnim.end(); + if (visibility != getVisibility()) { + if (mVisibilityAnim != null) { + mVisibilityAnim.end(); + } + super.setVisibility(visibility); } - super.setVisibility(visibility); } public boolean showOverflowMenu() { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 1767d68..83ac896 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -15,7 +15,7 @@ */ package com.android.internal.widget; -import com.android.internal.R; +import com.android.internal.view.ActionBarPolicy; import android.animation.Animator; import android.animation.ObjectAnimator; @@ -23,7 +23,6 @@ import android.animation.TimeInterpolator; import android.app.ActionBar; import android.content.Context; import android.content.res.Configuration; -import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.text.TextUtils.TruncateAt; import android.view.Gravity; @@ -55,6 +54,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView private boolean mAllowCollapse; int mMaxTabWidth; + int mStackedTabMaxWidth; private int mContentHeight; private int mSelectedTabIndex; @@ -69,10 +69,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView super(context); setHorizontalScrollBarEnabled(false); - TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar, - com.android.internal.R.attr.actionBarStyle, 0); - setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); - a.recycle(); + ActionBarPolicy abp = ActionBarPolicy.get(context); + setContentHeight(abp.getTabContainerHeight()); + mStackedTabMaxWidth = abp.getStackedTabMaxWidth(); mTabLayout = createTabLayout(); addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, @@ -93,6 +92,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView } else { mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; } + mMaxTabWidth = Math.min(mMaxTabWidth, mStackedTabMaxWidth); } else { mMaxTabWidth = -1; } @@ -187,6 +187,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView final LinearLayout tabLayout = new LinearLayout(getContext(), null, com.android.internal.R.attr.actionBarTabBarStyle); tabLayout.setMeasureWithLargestChildEnabled(true); + tabLayout.setGravity(Gravity.CENTER); tabLayout.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); return tabLayout; @@ -205,12 +206,11 @@ public class ScrollingTabContainerView extends HorizontalScrollView protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); + ActionBarPolicy abp = ActionBarPolicy.get(getContext()); // Action bar can change size on configuration changes. // Reread the desired height from the theme-specified style. - TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar, - com.android.internal.R.attr.actionBarStyle, 0); - setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); - a.recycle(); + setContentHeight(abp.getTabContainerHeight()); + mStackedTabMaxWidth = abp.getStackedTabMaxWidth(); } public void animateToVisibility(int visibility) { |
