diff options
Diffstat (limited to 'core/java/android')
53 files changed, 1485 insertions, 395 deletions
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 5f7a17d..27a03b6 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -54,6 +54,10 @@ interface IAccessibilityServiceConnection { int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long threadId); + boolean computeClickPointInScreen(int accessibilityWindowId, long accessibilityNodeId, + int interactionId, IAccessibilityInteractionConnectionCallback callback, + long threadId); + AccessibilityWindowInfo getWindow(int windowId); List<AccessibilityWindowInfo> getWindows(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 3e03893..9486a72 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2656,17 +2656,24 @@ public class ActivityManager { /** * Start an activity in this task. Brings the task to the foreground. If this task - * is not currently active (that is, its id < 0), then the activity being started - * needs to be started as a new task and the Intent's ComponentName must match the - * base ComponenentName of the recent task entry. Otherwise, the activity being - * started must <b>not</b> be launched as a new task -- not through explicit intent - * flags nor implicitly as the singleTask or singleInstance launch modes. + * is not currently active (that is, its id < 0), then a new activity for the given + * Intent will be launched as the root of the task and the task brought to the + * foreground. Otherwise, if this task is currently active and the Intent does not specify + * an activity to launch in a new task, then a new activity for the given Intent will + * be launched on top of the task and the task brought to the foreground. If this + * task is currently active and the Intent specifies {@link Intent#FLAG_ACTIVITY_NEW_TASK} + * or would otherwise be launched in to a new task, then the activity not launched but + * this task be brought to the foreground and a new intent delivered to the top + * activity if appropriate. * - * <p>See {@link Activity#startActivity(android.content.Intent, android.os.Bundle) - * Activity.startActivity} for more information.</p> + * <p>In other words, you generally want to use an Intent here that does not specify + * {@link Intent#FLAG_ACTIVITY_NEW_TASK} or {@link Intent#FLAG_ACTIVITY_NEW_DOCUMENT}, + * and let the system do the right thing.</p> * * @param intent The Intent describing the new activity to be launched on the task. * @param options Optional launch options. + * + * @see Activity#startActivity(android.content.Intent, android.os.Bundle) */ public void startActivity(Context context, Intent intent, Bundle options) { ActivityThread thread = ActivityThread.currentActivityThread(); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 1f7e450..394b183 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -509,8 +509,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM case ACTIVITY_PAUSED_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - PersistableBundle persistentState = data.readPersistableBundle(); - activityPaused(token, persistentState); + activityPaused(token); reply.writeNoException(); return true; } @@ -2829,13 +2828,12 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException + public void activityPaused(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); - data.writePersistableBundle(persistentState); mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 9dd4605..213c7f6 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -84,13 +84,13 @@ public class ActivityOptions { * Initial width of the animation. * @hide */ - public static final String KEY_ANIM_START_WIDTH = "android:animStartWidth"; + public static final String KEY_ANIM_WIDTH = "android:animWidth"; /** * Initial height of the animation. * @hide */ - public static final String KEY_ANIM_START_HEIGHT = "android:animStartHeight"; + public static final String KEY_ANIM_HEIGHT = "android:animHeight"; /** * Callback for when animation is started. @@ -140,8 +140,8 @@ public class ActivityOptions { private Bitmap mThumbnail; private int mStartX; private int mStartY; - private int mStartWidth; - private int mStartHeight; + private int mWidth; + private int mHeight; private IRemoteCallback mAnimationStartedListener; private ResultReceiver mTransitionReceiver; private boolean mIsReturning; @@ -238,13 +238,13 @@ public class ActivityOptions { * defines the coordinate space for <var>startX</var> and <var>startY</var>. * @param startX The x starting location of the new activity, relative to <var>source</var>. * @param startY The y starting location of the activity, relative to <var>source</var>. - * @param startWidth The initial width of the new activity. - * @param startHeight The initial height of the new activity. + * @param width The initial width of the new activity. + * @param height The initial height of the new activity. * @return Returns a new ActivityOptions object that you can use to * supply these options as the options Bundle when starting an activity. */ public static ActivityOptions makeScaleUpAnimation(View source, - int startX, int startY, int startWidth, int startHeight) { + int startX, int startY, int width, int height) { ActivityOptions opts = new ActivityOptions(); opts.mPackageName = source.getContext().getPackageName(); opts.mAnimationType = ANIM_SCALE_UP; @@ -252,8 +252,8 @@ public class ActivityOptions { source.getLocationOnScreen(pts); opts.mStartX = pts[0] + startX; opts.mStartY = pts[1] + startY; - opts.mStartWidth = startWidth; - opts.mStartHeight = startHeight; + opts.mWidth = width; + opts.mHeight = height; return opts; } @@ -359,9 +359,10 @@ public class ActivityOptions { * @hide */ public static ActivityOptions makeThumbnailAspectScaleUpAnimation(View source, - Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { - return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener, - true); + Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, + OnAnimationStartedListener listener) { + return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, + targetWidth, targetHeight, listener, true); } /** @@ -382,13 +383,15 @@ public class ActivityOptions { * @hide */ public static ActivityOptions makeThumbnailAspectScaleDownAnimation(View source, - Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) { - return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, listener, - false); + Bitmap thumbnail, int startX, int startY, int targetWidth, int targetHeight, + OnAnimationStartedListener listener) { + return makeAspectScaledThumbnailAnimation(source, thumbnail, startX, startY, + targetWidth, targetHeight, listener, false); } private static ActivityOptions makeAspectScaledThumbnailAnimation(View source, Bitmap thumbnail, - int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp) { + int startX, int startY, int targetWidth, int targetHeight, + OnAnimationStartedListener listener, boolean scaleUp) { ActivityOptions opts = new ActivityOptions(); opts.mPackageName = source.getContext().getPackageName(); opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_ASPECT_SCALE_UP : @@ -398,6 +401,8 @@ public class ActivityOptions { source.getLocationOnScreen(pts); opts.mStartX = pts[0] + startX; opts.mStartY = pts[1] + startY; + opts.mWidth = targetWidth; + opts.mHeight = targetHeight; opts.setOnAnimationStartedListener(source.getHandler(), listener); return opts; } @@ -515,11 +520,6 @@ public class ActivityOptions { return opts; } - @Deprecated - public static ActivityOptions makeLaunchTaskBehindAnimation() { - return makeTaskLaunchBehind(); - } - /** @hide */ public boolean getLaunchTaskBehind() { return mAnimationType == ANIM_LAUNCH_TASK_BEHIND; @@ -543,8 +543,8 @@ public class ActivityOptions { case ANIM_SCALE_UP: mStartX = opts.getInt(KEY_ANIM_START_X, 0); mStartY = opts.getInt(KEY_ANIM_START_Y, 0); - mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0); - mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0); + mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); + mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); break; case ANIM_THUMBNAIL_SCALE_UP: @@ -554,6 +554,8 @@ public class ActivityOptions { mThumbnail = (Bitmap) opts.getParcelable(KEY_ANIM_THUMBNAIL); mStartX = opts.getInt(KEY_ANIM_START_X, 0); mStartY = opts.getInt(KEY_ANIM_START_Y, 0); + mWidth = opts.getInt(KEY_ANIM_WIDTH, 0); + mHeight = opts.getInt(KEY_ANIM_HEIGHT, 0); mAnimationStartedListener = IRemoteCallback.Stub.asInterface( opts.getBinder(KEY_ANIM_START_LISTENER)); break; @@ -605,13 +607,13 @@ public class ActivityOptions { } /** @hide */ - public int getStartWidth() { - return mStartWidth; + public int getWidth() { + return mWidth; } /** @hide */ - public int getStartHeight() { - return mStartHeight; + public int getHeight() { + return mHeight; } /** @hide */ @@ -690,8 +692,8 @@ public class ActivityOptions { case ANIM_SCALE_UP: mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; - mStartWidth = otherOptions.mStartWidth; - mStartHeight = otherOptions.mStartHeight; + mWidth = otherOptions.mWidth; + mHeight = otherOptions.mHeight; if (mAnimationStartedListener != null) { try { mAnimationStartedListener.sendResult(null); @@ -707,6 +709,8 @@ public class ActivityOptions { mThumbnail = otherOptions.mThumbnail; mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; + mWidth = otherOptions.mWidth; + mHeight = otherOptions.mHeight; if (mAnimationStartedListener != null) { try { mAnimationStartedListener.sendResult(null); @@ -755,8 +759,8 @@ public class ActivityOptions { case ANIM_SCALE_UP: b.putInt(KEY_ANIM_START_X, mStartX); b.putInt(KEY_ANIM_START_Y, mStartY); - b.putInt(KEY_ANIM_START_WIDTH, mStartWidth); - b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight); + b.putInt(KEY_ANIM_WIDTH, mWidth); + b.putInt(KEY_ANIM_HEIGHT, mHeight); break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: @@ -765,6 +769,8 @@ public class ActivityOptions { b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); b.putInt(KEY_ANIM_START_X, mStartX); b.putInt(KEY_ANIM_START_Y, mStartY); + b.putInt(KEY_ANIM_WIDTH, mWidth); + b.putInt(KEY_ANIM_HEIGHT, mHeight); b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener != null ? mAnimationStartedListener.asBinder() : null); break; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4f2a3bc..3a39900 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -563,11 +563,11 @@ public final class ActivityThread { } public final void schedulePauseActivity(IBinder token, boolean finished, - boolean userLeaving, int configChanges) { + boolean userLeaving, int configChanges, boolean dontReport) { sendMessage( finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY, token, - (userLeaving ? 1 : 0), + (userLeaving ? 1 : 0) | (dontReport ? 2 : 0), configChanges); } @@ -1283,13 +1283,15 @@ public final class ActivityThread { } break; case PAUSE_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); - handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2); + handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2, + (msg.arg1&2) != 0); maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case PAUSE_ACTIVITY_FINISHING: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); - handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2); + handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2, + (msg.arg1&1) != 0); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case STOP_ACTIVITY_SHOW: @@ -3142,7 +3144,7 @@ public final class ActivityThread { } private void handlePauseActivity(IBinder token, boolean finished, - boolean userLeaving, int configChanges) { + boolean userLeaving, int configChanges, boolean dontReport) { ActivityClientRecord r = mActivities.get(token); if (r != null) { //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r); @@ -3159,9 +3161,11 @@ public final class ActivityThread { } // Tell the activity manager we have paused. - try { - ActivityManagerNative.getDefault().activityPaused(token, r.persistentState); - } catch (RemoteException ex) { + if (!dontReport) { + try { + ActivityManagerNative.getDefault().activityPaused(token); + } catch (RemoteException ex) { + } } mSomeActivitiesChanged = true; } @@ -4283,6 +4287,8 @@ public final class ActivityThread { AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + Message.updateCheckRecycle(data.appInfo.targetSdkVersion); + /* * Before spawning a new process, reset the time zone to be the system time zone. * This needs to be done because the system time zone could have changed after the diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 9e80a4b..9f49194 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -205,6 +205,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { private boolean mIsStartingTransition; private ArrayList<GhostViewListeners> mGhostViewListeners = new ArrayList<GhostViewListeners>(); + private ArrayMap<View, Float> mOriginalAlphas = new ArrayMap<View, Float>(); public ActivityTransitionCoordinator(Window window, ArrayList<String> allSharedElementNames, @@ -580,6 +581,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { mWindow = null; mSharedElements.clear(); mTransitioningViews.clear(); + mOriginalAlphas.clear(); mResultReceiver = null; mPendingTransition = null; mListener = null; @@ -589,10 +591,27 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return getWindow().getTransitionBackgroundFadeDuration(); } - protected static void setTransitionAlpha(ArrayList<View> views, float alpha) { + protected void hideViews(ArrayList<View> views) { int count = views.size(); for (int i = 0; i < count; i++) { - views.get(i).setTransitionAlpha(alpha); + View view = views.get(i); + if (!mOriginalAlphas.containsKey(view)) { + mOriginalAlphas.put(view, view.getAlpha()); + } + view.setAlpha(0f); + view.setTransitionAlpha(0f); + } + } + + protected void showViews(ArrayList<View> views) { + int count = views.size(); + for (int i = 0; i < count; i++) { + View view = views.get(i); + Float alpha = mOriginalAlphas.remove(view); + if (alpha != null) { + view.setAlpha(alpha); + view.setTransitionAlpha(1f); + } } } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 9cd6d49..404268c 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1693,7 +1693,7 @@ final class ApplicationPackageManager extends PackageManager { if (dr == null) { dr = itemInfo.loadDefaultIcon(this); } - return getUserBadgedDrawableForDensity(dr, new UserHandle(mContext.getUserId()), null, 0); + return getUserBadgedIcon(dr, new UserHandle(mContext.getUserId())); } private Drawable getBadgedDrawable(Drawable drawable, Drawable badgeDrawable, diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 63e8707..0123e16 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -78,7 +78,8 @@ public abstract class ApplicationThreadNative extends Binder boolean finished = data.readInt() != 0; boolean userLeaving = data.readInt() != 0; int configChanges = data.readInt(); - schedulePauseActivity(b, finished, userLeaving, configChanges); + boolean dontReport = data.readInt() != 0; + schedulePauseActivity(b, finished, userLeaving, configChanges, dontReport); return true; } @@ -689,13 +690,14 @@ class ApplicationThreadProxy implements IApplicationThread { } public final void schedulePauseActivity(IBinder token, boolean finished, - boolean userLeaving, int configChanges) throws RemoteException { + boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); data.writeInt(finished ? 1 : 0); data.writeInt(userLeaving ? 1 :0); data.writeInt(configChanges); + data.writeInt(dontReport ? 1 : 0); mRemote.transact(SCHEDULE_PAUSE_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index f432c49..f38c108 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -112,9 +112,9 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { protected void viewsReady(ArrayMap<String, View> sharedElements) { super.viewsReady(sharedElements); mIsReadyForTransition = true; - setTransitionAlpha(mSharedElements, 0); + hideViews(mSharedElements); if (getViewsTransition() != null) { - setTransitionAlpha(mTransitioningViews, 0); + hideViews(mTransitioningViews); } if (mIsReturning) { sendSharedElementDestination(); @@ -240,7 +240,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { if (!mIsCanceled) { mIsCanceled = true; if (getViewsTransition() == null || mIsViewsTransitionStarted) { - setTransitionAlpha(mSharedElements, 1); + showViews(mSharedElements); } else { mTransitioningViews.addAll(mSharedElements); } @@ -300,7 +300,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { // Now start shared element transition ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState, mSharedElementNames); - setTransitionAlpha(mSharedElements, 1); + showViews(mSharedElements); scheduleSetSharedElementEnd(sharedElementSnapshots); ArrayList<SharedElementOriginalState> originalImageViewState = setSharedElementState(sharedElementState, sharedElementSnapshots); @@ -411,7 +411,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { @Override public void onTransitionStart(Transition transition) { mEnterViewsTransition = transition; - setTransitionAlpha(mTransitioningViews, 1); + showViews(mTransitioningViews); super.onTransitionStart(transition); } diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index a59a927..982bbc4 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -126,8 +126,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } public void resetViews() { - setTransitionAlpha(mTransitioningViews, 1); - setTransitionAlpha(mSharedElements, 1); + showViews(mTransitioningViews); + showViews(mSharedElements); mIsHidden = true; if (!mIsReturning && getDecor() != null) { getDecor().suppressLayout(false); @@ -187,7 +187,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private void hideSharedElements() { moveSharedElementsFromOverlay(); if (!mIsHidden) { - setTransitionAlpha(mSharedElements, 0); + hideViews(mSharedElements); } mSharedElementsHidden = true; finishIfNecessary(); @@ -296,7 +296,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { transition.removeListener(this); exitTransitionComplete(); if (mIsHidden) { - setTransitionAlpha(mTransitioningViews, 1); + showViews(mTransitioningViews); } if (mSharedElementBundle != null) { delayCancel(); @@ -323,7 +323,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { transition.removeListener(this); sharedElementTransitionComplete(); if (mIsHidden) { - setTransitionAlpha(mSharedElements, 1); + showViews(mSharedElements); } } }); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 99428e8..9483680 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -111,7 +111,7 @@ public interface IActivityManager extends IInterface { public void activityResumed(IBinder token) throws RemoteException; public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) throws RemoteException; - public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException; + public void activityPaused(IBinder token) throws RemoteException; public void activityStopped(IBinder token, Bundle state, PersistableBundle persistentState, CharSequence description) throws RemoteException; public void activitySlept(IBinder token) throws RemoteException; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index a7546d9..f53075c 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -49,7 +49,7 @@ import java.util.Map; */ public interface IApplicationThread extends IInterface { void schedulePauseActivity(IBinder token, boolean finished, boolean userLeaving, - int configChanges) throws RemoteException; + int configChanges, boolean dontReport) throws RemoteException; void scheduleStopActivity(IBinder token, boolean showWindow, int configChanges) throws RemoteException; void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException; diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl index 181eb63..3b5900b 100644 --- a/core/java/android/app/IWallpaperManager.aidl +++ b/core/java/android/app/IWallpaperManager.aidl @@ -16,6 +16,7 @@ package android.app; +import android.graphics.Rect; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.app.IWallpaperManagerCallback; @@ -73,6 +74,11 @@ interface IWallpaperManager { int getHeightHint(); /** + * Sets extra padding that we would like the wallpaper to have outside of the display. + */ + void setDisplayPadding(in Rect padding); + + /** * Returns the name of the wallpaper. Private API. */ String getName(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 1083943..2476c4b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -545,8 +545,26 @@ public class Notification implements Parcelable */ public int visibility; + /** + * Notification visibility: Show this notification in its entirety on all lockscreens. + * + * {@see #visibility} + */ public static final int VISIBILITY_PUBLIC = 1; + + /** + * Notification visibility: Show this notification on all lockscreens, but conceal sensitive or + * private information on secure lockscreens. + * + * {@see #visibility} + */ public static final int VISIBILITY_PRIVATE = 0; + + /** + * Notification visibility: Do not reveal any part of this notification on a secure lockscreen. + * + * {@see #visibility} + */ public static final int VISIBILITY_SECRET = -1; /** @@ -3386,8 +3404,16 @@ public class Notification implements Parcelable */ public static abstract class Style { private CharSequence mBigContentTitle; - private CharSequence mSummaryText = null; - private boolean mSummaryTextSet = false; + + /** + * @hide + */ + protected CharSequence mSummaryText = null; + + /** + * @hide + */ + protected boolean mSummaryTextSet = false; protected Builder mBuilder; @@ -3679,6 +3705,11 @@ public class Notification implements Parcelable * @see Notification#bigContentView */ public static class BigTextStyle extends Style { + + private static final int MAX_LINES = 13; + private static final int LINES_CONSUMED_BY_ACTIONS = 3; + private static final int LINES_CONSUMED_BY_SUMMARY = 2; + private CharSequence mBigText; public BigTextStyle() { @@ -3745,6 +3776,7 @@ public class Notification implements Parcelable contentView.setTextViewText(R.id.big_text, mBuilder.processLegacyText(mBigText)); contentView.setViewVisibility(R.id.big_text, View.VISIBLE); + contentView.setInt(R.id.big_text, "setMaxLines", calculateMaxLines()); contentView.setViewVisibility(R.id.text2, View.GONE); applyTopPadding(contentView); @@ -3756,6 +3788,24 @@ public class Notification implements Parcelable return contentView; } + private int calculateMaxLines() { + int lineCount = MAX_LINES; + boolean hasActions = mBuilder.mActions.size() > 0; + boolean hasSummary = (mSummaryTextSet ? mSummaryText : mBuilder.mSubText) != null; + if (hasActions) { + lineCount -= LINES_CONSUMED_BY_ACTIONS; + } + if (hasSummary) { + lineCount -= LINES_CONSUMED_BY_SUMMARY; + } + + // If we have less top padding at the top, we can fit less lines. + if (!mBuilder.mHasThreeLines) { + lineCount--; + } + return lineCount; + } + /** * @hide */ diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 48ff5b6..8bfe6d3 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.SystemApi; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -951,6 +952,48 @@ public class WallpaperManager { } /** + * Specify extra padding that the wallpaper should have outside of the display. + * That is, the given padding supplies additional pixels the wallpaper should extend + * outside of the display itself. + * @param padding The number of pixels the wallpaper should extend beyond the display, + * on its left, top, right, and bottom sides. + * @hide + */ + @SystemApi + public void setDisplayPadding(Rect padding) { + try { + if (sGlobals.mService == null) { + Log.w(TAG, "WallpaperService not running"); + } else { + sGlobals.mService.setDisplayPadding(padding); + } + } catch (RemoteException e) { + // Ignore + } + } + + /** + * Apply a raw offset to the wallpaper window. Should only be used in + * combination with {@link #setDisplayPadding(android.graphics.Rect)} when you + * have ensured that the wallpaper will extend outside of the display area so that + * it can be moved without leaving part of the display uncovered. + * @param x The offset, in pixels, to apply to the left edge. + * @param y The offset, in pixels, to apply to the top edge. + * @hide + */ + @SystemApi + public void setDisplayOffset(IBinder windowToken, int x, int y) { + try { + //Log.v(TAG, "Sending new wallpaper display offsets from app..."); + WindowManagerGlobal.getWindowSession().setWallpaperDisplayOffset( + windowToken, x, y); + //Log.v(TAG, "...app returning after sending display offset!"); + } catch (RemoteException e) { + // Ignore. + } + } + + /** * Set the position of the current wallpaper within any larger space, when * that wallpaper is visible behind the given window. The X and Y offsets * are floating point numbers ranging from 0 to 1, representing where the diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 112fc82..926a348 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -441,13 +441,13 @@ public class DevicePolicyManager { * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from a * managed profile to its parent. */ - public static int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001; + public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 0x0001; /** * Flag used by {@link #addCrossProfileIntentFilter} to allow access of certain intents from the * parent to its managed profile. */ - public static int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; + public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 0x0002; /** * Return true if the given administrator component is currently diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index fde8b2e..2853c58 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -639,12 +639,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 { * @param authorities the semi-colon separated authorities of the ContentProvider. */ protected final void setAuthorities(String authorities) { - if (authorities.indexOf(';') == -1) { - mAuthority = authorities; - mAuthorities = null; - } else { - mAuthority = null; - mAuthorities = authorities.split(";"); + if (authorities != null) { + if (authorities.indexOf(';') == -1) { + mAuthority = authorities; + mAuthorities = null; + } else { + mAuthority = null; + mAuthorities = authorities.split(";"); + } } } @@ -653,9 +655,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (mAuthority != null) { return mAuthority.equals(authority); } - int length = mAuthorities.length; - for (int i = 0; i < length; i++) { - if (mAuthorities[i].equals(authority)) return true; + if (mAuthorities != null) { + int length = mAuthorities.length; + for (int i = 0; i < length; i++) { + if (mAuthorities[i].equals(authority)) return true; + } } return false; } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index dbf49c5..8d3126d 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -255,16 +255,22 @@ public class ActivityInfo extends ComponentInfo * Bit in {@link #flags}: If set, a task rooted at this activity will have its * baseIntent replaced by the activity immediately above this. Each activity may further * relinquish its identity to the activity above it using this flag. Set from the - * android.R.attr#relinquishTaskIdentity attribute. + * {@link android.R.attr#relinquishTaskIdentity} attribute. */ public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000; /** * Bit in {@link #flags} indicating that tasks started with this activity are to be * removed from the recent list of tasks when the last activity in the task is finished. - * {@link android.R.attr#autoRemoveFromRecents} + * Corresponds to {@link android.R.attr#autoRemoveFromRecents} */ public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000; /** + * Bit in {@link #flags} indicating that this activity can start is creation/resume + * while the previous activity is still pausing. Corresponds to + * {@link android.R.attr#resumeWhilePausing} + */ + public static final int FLAG_RESUME_WHILE_PAUSING = 0x4000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the primary user. Only works with broadcast receivers. Set from the * android.R.attr#primaryUserOnly attribute. diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index f9370b3..d49bc50 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -278,21 +278,21 @@ public class LauncherApps { /** - * Adds a callback for changes to packages in current and managed profiles. + * Registers a callback for changes to packages in current and managed profiles. * - * @param callback The callback to add. + * @param callback The callback to register. */ - public void addCallback(Callback callback) { - addCallback(callback, null); + public void registerCallback(Callback callback) { + registerCallback(callback, null); } /** - * Adds a callback for changes to packages in current and managed profiles. + * Registers a callback for changes to packages in current and managed profiles. * - * @param callback The callback to add. + * @param callback The callback to register. * @param handler that should be used to post callbacks on, may be null. */ - public void addCallback(Callback callback, Handler handler) { + public void registerCallback(Callback callback, Handler handler) { synchronized (this) { if (callback != null && !mCallbacks.contains(callback)) { boolean addedFirstCallback = mCallbacks.size() == 0; @@ -308,12 +308,12 @@ public class LauncherApps { } /** - * Removes a callback that was previously added. + * Unregisters a callback that was previously registered. * - * @param callback The callback to remove. - * @see #addCallback(Callback) + * @param callback The callback to unregister. + * @see #registerCallback(Callback) */ - public void removeCallback(Callback callback) { + public void unregisterCallback(Callback callback) { synchronized (this) { removeCallbackLocked(callback); if (mCallbacks.size() == 0) { @@ -500,44 +500,12 @@ public class LauncherApps { } } - /** Remove after unbundled apps have migrated STOP SHIP */ - public static abstract class OnAppsChangedCallback extends Callback { - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public void addOnAppsChangedCallback(OnAppsChangedCallback callback) { - addCallback(callback, null); - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public void addOnAppsChangedCallback(OnAppsChangedCallback callback, Handler handler) { - addCallback(callback, handler); - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) { - removeCallback(callback); - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public void startActivityForProfile(ComponentName component, UserHandle user, Rect sourceBounds, - Bundle opts) { - startMainActivity(component, user, sourceBounds, opts); - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public void showAppDetailsForProfile(ComponentName component, UserHandle user, - Rect sourceBounds, Bundle opts) { - startAppDetailsActivity(component, user, sourceBounds, opts); - } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public boolean isPackageEnabledForProfile(String packageName, UserHandle user) { - return isPackageEnabled(packageName, user); + /** STOPSHIP remove when launcher 3 has been updated */ + public void addCallback(Callback callback) { + registerCallback(callback); } - - /** Remove after unbundled apps have migrated STOP SHIP */ - public boolean isActivityEnabledForProfile(ComponentName component, UserHandle user) { - return isActivityEnabled(component, user); + /** STOPSHIP remove when launcher 3 has been updated */ + public void removeCallback(Callback callback) { + unregisterCallback(callback); } } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0a211cf..06d4c4a 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -285,6 +285,9 @@ public class PackageInstaller { * * @throws IOException if parameters were unsatisfiable, such as lack of * disk space or unavailable media. + * @throws SecurityException when installation services are unavailable, + * such as when called from a restricted user. + * @throws IllegalArgumentException when {@link SessionParams} is invalid. * @return positive, non-zero unique ID that represents the created session. * This ID remains consistent across device reboots until the * session is finalized. IDs are not reused during a given boot. @@ -303,6 +306,11 @@ public class PackageInstaller { /** * Open an existing session to actively perform work. To succeed, the caller * must be the owner of the install session. + * + * @throws IOException if parameters were unsatisfiable, such as lack of + * disk space or unavailable media. + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public @NonNull Session openSession(int sessionId) throws IOException { try { @@ -319,6 +327,9 @@ public class PackageInstaller { * Update the icon representing the app being installed in a specific * session. This should be roughly * {@link ActivityManager#getLauncherLargeIconSize()} in both dimensions. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public void updateSessionAppIcon(int sessionId, @Nullable Bitmap appIcon) { try { @@ -331,6 +342,9 @@ public class PackageInstaller { /** * Update the label representing the app being installed in a specific * session. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. */ public void updateSessionAppLabel(int sessionId, @Nullable CharSequence appLabel) { try { @@ -341,6 +355,15 @@ public class PackageInstaller { } } + /** + * Completely abandon the given session, destroying all staged data and + * rendering it invalid. Abandoned sessions will be reported to + * {@link SessionCallback} listeners as failures. This is equivalent to + * opening the session and calling {@link Session#abandon()}. + * + * @throws SecurityException when the caller does not own the session, or + * the session is invalid. + */ public void abandonSession(int sessionId) { try { mInstaller.abandonSession(sessionId); @@ -350,7 +373,11 @@ public class PackageInstaller { } /** - * Return details for a specific session. + * Return details for a specific session. No special permissions are + * required to retrieve these details. + * + * @return details for the requested session, or {@code null} if the session + * does not exist. */ public @Nullable SessionInfo getSessionInfo(int sessionId) { try { @@ -361,7 +388,7 @@ public class PackageInstaller { } /** - * Return list of all active install sessions, regardless of the installer. + * Return list of all known install sessions, regardless of the installer. */ public @NonNull List<SessionInfo> getAllSessions() { final ApplicationInfo info = mContext.getApplicationInfo(); @@ -379,7 +406,7 @@ public class PackageInstaller { } /** - * Return list of all install sessions owned by the calling app. + * Return list of all known install sessions owned by the calling app. */ public @NonNull List<SessionInfo> getMySessions() { try { @@ -547,7 +574,8 @@ public class PackageInstaller { } /** - * Register to watch for session lifecycle events. + * Register to watch for session lifecycle events. No special permissions + * are required to watch for these events. */ public void registerSessionCallback(@NonNull SessionCallback callback) { registerSessionCallback(callback, new Handler()); @@ -560,7 +588,8 @@ public class PackageInstaller { } /** - * Register to watch for session lifecycle events. + * Register to watch for session lifecycle events. No special permissions + * are required to watch for these events. * * @param handler to dispatch callback events through, otherwise uses * calling thread. @@ -593,7 +622,7 @@ public class PackageInstaller { } /** - * Unregister an existing callback. + * Unregister a previously registered callback. */ public void unregisterSessionCallback(@NonNull SessionCallback callback) { synchronized (mDelegates) { @@ -686,6 +715,12 @@ public class PackageInstaller { * start at the beginning of the file. * @param lengthBytes total size of the file being written, used to * preallocate the underlying disk space, or -1 if unknown. + * The system may clear various caches as needed to allocate + * this space. + * @throws IOException if trouble opening the file for writing, such as + * lack of disk space or unavailable media. + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { @@ -719,6 +754,9 @@ public class PackageInstaller { * <p> * This returns all names which have been previously written through * {@link #openWrite(String, long, long)} as part of this session. + * + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull String[] getNames() throws IOException { try { @@ -738,6 +776,9 @@ public class PackageInstaller { * through {@link #openWrite(String, long, long)} as part of this * session. For example, this stream may be used to calculate a * {@link MessageDigest} of a written APK before committing. + * + * @throws SecurityException if called after the session has been + * committed or abandoned. */ public @NonNull InputStream openRead(@NonNull String name) throws IOException { try { @@ -759,6 +800,9 @@ public class PackageInstaller { * Once this method is called, no additional mutations may be performed * on the session. If the device reboots before the session has been * finalized, you may commit the session again. + * + * @throws SecurityException if streams opened through + * {@link #openWrite(String, long, long)} are still open. */ public void commit(@NonNull IntentSender statusReceiver) { try { @@ -783,7 +827,9 @@ public class PackageInstaller { /** * Completely abandon this session, destroying all staged data and - * rendering it invalid. + * rendering it invalid. Abandoned sessions will be reported to + * {@link SessionCallback} listeners as failures. This is equivalent to + * opening the session and calling {@link Session#abandon()}. */ public void abandon() { try { @@ -937,6 +983,18 @@ public class PackageInstaller { } /** {@hide} */ + public void setInstallFlagsInternal() { + installFlags |= PackageManager.INSTALL_INTERNAL; + installFlags &= ~PackageManager.INSTALL_EXTERNAL; + } + + /** {@hide} */ + public void setInstallFlagsExternal() { + installFlags |= PackageManager.INSTALL_EXTERNAL; + installFlags &= ~PackageManager.INSTALL_INTERNAL; + } + + /** {@hide} */ public void dump(IndentingPrintWriter pw) { pw.printPair("mode", mode); pw.printHexPair("installFlags", installFlags); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e87adda..36d15a0 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -201,12 +201,6 @@ public abstract class PackageManager { public static final int MATCH_DEFAULT_ONLY = 0x00010000; /** - * Resolution and querying flag: do not resolve intents cross-profile. - * @hide - */ - public static final int NO_CROSS_PROFILE = 0x00020000; - - /** * Flag for {@link addCrossProfileIntentFilter}: if this flag is set: * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current * profile will be skipped. @@ -976,6 +970,14 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device includes at least one form of audio + * output, such as speakers, audio jack or streaming over bluetooth + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device is capable of communicating with * other devices via Bluetooth. */ @@ -1546,6 +1548,15 @@ public abstract class PackageManager { public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device has all of the inputs necessary to be considered a compatible game controller, or + * includes a compatible game controller in the box. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_GAMEPAD = "android.hardware.gamepad"; + + + /** * Action to external storage service to clean out removed apps. * @hide */ @@ -2423,7 +2434,6 @@ public abstract class PackageManager { * @see #MATCH_DEFAULT_ONLY * @see #GET_INTENT_FILTERS * @see #GET_RESOLVED_FILTER - * @see #NO_CROSS_PROFILE * @hide */ public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 6d40dcf..e0fd532 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3110,6 +3110,12 @@ public class PackageParser { false)) { a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; } + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_resumeWhilePausing, + false)) { + a.info.flags |= ActivityInfo.FLAG_RESUME_WHILE_PAUSING; + } } else { a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; a.info.configChanges = 0; diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 7a49eb5..7390e2b 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -480,7 +480,7 @@ public class SoundTrigger { int capturePreambleMs = in.readInt(); boolean triggerInData = in.readByte() == 1; AudioFormat captureFormat = null; - if (triggerInData) { + if (in.readByte() == 1) { int sampleRate = in.readInt(); int encoding = in.readInt(); int channelMask = in.readInt(); @@ -508,7 +508,8 @@ public class SoundTrigger { dest.writeInt(captureSession); dest.writeInt(captureDelayMs); dest.writeInt(capturePreambleMs); - if (triggerInData && (captureFormat != null)) { + dest.writeByte((byte) (triggerInData ? 1 : 0)); + if (captureFormat != null) { dest.writeByte((byte)1); dest.writeInt(captureFormat.getSampleRate()); dest.writeInt(captureFormat.getEncoding()); diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java index 022a106..0acf24b 100644 --- a/core/java/android/os/FileBridge.java +++ b/core/java/android/os/FileBridge.java @@ -75,6 +75,13 @@ public class FileBridge extends Thread { return mClosed; } + public void forceClose() { + IoUtils.closeQuietly(mTarget); + IoUtils.closeQuietly(mServer); + IoUtils.closeQuietly(mClient); + mClosed = true; + } + public void setTargetFile(FileDescriptor target) { mTarget = target; } @@ -89,7 +96,6 @@ public class FileBridge extends Thread { try { while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); - if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); @@ -118,15 +124,10 @@ public class FileBridge extends Thread { } } - } catch (ErrnoException e) { - Log.wtf(TAG, "Failed during bridge", e); - } catch (IOException e) { + } catch (ErrnoException | IOException e) { Log.wtf(TAG, "Failed during bridge", e); } finally { - IoUtils.closeQuietly(mTarget); - IoUtils.closeQuietly(mServer); - IoUtils.closeQuietly(mClient); - mClosed = true; + forceClose(); } } @@ -151,6 +152,7 @@ public class FileBridge extends Thread { writeCommandAndBlock(CMD_CLOSE, "close()"); } finally { IoBridge.closeAndSignalBlockedThreads(mClient); + IoUtils.closeQuietly(mClientPfd); } } diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java index 1a5811c..b6b70cc 100644 --- a/core/java/android/os/Message.java +++ b/core/java/android/os/Message.java @@ -113,6 +113,8 @@ public final class Message implements Parcelable { private static final int MAX_POOL_SIZE = 50; + private static boolean gCheckRecycle = true; + /** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. @@ -256,6 +258,13 @@ public final class Message implements Parcelable { return m; } + /** @hide */ + public static void updateCheckRecycle(int targetSdkVersion) { + if (targetSdkVersion < Build.VERSION_CODES.L) { + gCheckRecycle = false; + } + } + /** * Return a Message instance to the global pool. * <p> @@ -266,8 +275,11 @@ public final class Message implements Parcelable { */ public void recycle() { if (isInUse()) { - throw new IllegalStateException("This message cannot be recycled because it " - + "is still in use."); + if (gCheckRecycle) { + throw new IllegalStateException("This message cannot be recycled because it " + + "is still in use."); + } + return; } recycleUnchecked(); } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index c3ac012..b2ebc31 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -468,6 +468,7 @@ public class Process { * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. * @param abi non-null the ABI this app should be started with. + * @param instructionSet null-ok the instruction set to use. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. @@ -482,11 +483,12 @@ public class Process { int targetSdkVersion, String seInfo, String abi, + String instructionSet, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, - abi, zygoteArgs); + abi, instructionSet, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); @@ -589,6 +591,7 @@ public class Process { * @param targetSdkVersion The target SDK version for the app. * @param seInfo null-ok SELinux information for the new process. * @param abi the ABI the process should use. + * @param instructionSet null-ok the instruction set to use. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason @@ -601,6 +604,7 @@ public class Process { int targetSdkVersion, String seInfo, String abi, + String instructionSet, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { @@ -660,6 +664,10 @@ public class Process { argsForZygote.add("--seinfo=" + seInfo); } + if (instructionSet != null) { + argsForZygote.add("--instruction-set=" + instructionSet); + } + argsForZygote.add(processClass); if (extraArgs != null) { diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 5fa1cc9..a5e86d8 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -167,8 +167,6 @@ public class CallLog { */ public static final String FEATURES = "features"; - /** Call had no associated features (e.g. voice-only). */ - public static final int FEATURES_NONE = 0x0; /** Call had video. */ public static final int FEATURES_VIDEO = 0x1; diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 4de5f41..8aa2689 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -468,19 +468,6 @@ public class AlwaysOnHotwordDetector { } /** - * FIXME: Remove once the prebuilts are updated. - * - * @hide - */ - @Deprecated - public Intent createIntentToEnroll() { - if (DBG) Slog.d(TAG, "createIntentToEnroll"); - synchronized (mLock) { - return getManageIntentLocked(MANAGE_ACTION_ENROLL); - } - } - - /** * Creates an intent to start the un-enrollment for the associated keyphrase. * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. * Starting re-enrollment is only valid if the keyphrase is already enrolled, @@ -502,19 +489,6 @@ public class AlwaysOnHotwordDetector { } /** - * FIXME: Remove once the prebuilts are updated. - * - * @hide - */ - @Deprecated - public Intent createIntentToUnEnroll() { - if (DBG) Slog.d(TAG, "createIntentToUnEnroll"); - synchronized (mLock) { - return getManageIntentLocked(MANAGE_ACTION_UN_ENROLL); - } - } - - /** * Creates an intent to start the re-enrollment for the associated keyphrase. * This intent must be invoked using {@link Activity#startActivityForResult(Intent, int)}. * Starting re-enrollment is only valid if the keyphrase is already enrolled, @@ -535,19 +509,6 @@ public class AlwaysOnHotwordDetector { } } - /** - * FIXME: Remove once the prebuilts are updated. - * - * @hide - */ - @Deprecated - public Intent createIntentToReEnroll() { - if (DBG) Slog.d(TAG, "createIntentToReEnroll"); - synchronized (mLock) { - return getManageIntentLocked(MANAGE_ACTION_RE_ENROLL); - } - } - private Intent getManageIntentLocked(int action) { if (mAvailability == STATE_INVALID) { throw new IllegalStateException("getManageIntent called on an invalid detector"); diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index faccde2..de527e9 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -16,6 +16,7 @@ package android.service.wallpaper; +import android.graphics.Rect; import android.view.MotionEvent; import android.os.Bundle; @@ -24,6 +25,7 @@ import android.os.Bundle; */ oneway interface IWallpaperEngine { void setDesiredSize(int width, int height); + void setDisplayPadding(in Rect padding); void setVisibility(boolean visible); void dispatchPointer(in MotionEvent event); void dispatchWallpaperCommand(String action, int x, int y, diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl index bc7a1d7..5fd0157 100644 --- a/core/java/android/service/wallpaper/IWallpaperService.aidl +++ b/core/java/android/service/wallpaper/IWallpaperService.aidl @@ -16,6 +16,7 @@ package android.service.wallpaper; +import android.graphics.Rect; import android.service.wallpaper.IWallpaperConnection; /** @@ -24,5 +25,5 @@ import android.service.wallpaper.IWallpaperConnection; oneway interface IWallpaperService { void attach(IWallpaperConnection connection, IBinder windowToken, int windowType, boolean isPreview, - int reqWidth, int reqHeight); + int reqWidth, int reqHeight, in Rect padding); } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index f3c26c8..26e9a30 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -16,6 +16,14 @@ package android.service.wallpaper; +import android.content.res.TypedArray; +import android.os.Build; +import android.os.SystemProperties; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.ViewRootImpl; +import android.view.WindowInsets; +import com.android.internal.R; import com.android.internal.os.HandlerCaller; import com.android.internal.view.BaseIWindow; import com.android.internal.view.BaseSurfaceHolder; @@ -56,6 +64,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; + /** * A wallpaper service is responsible for showing a live wallpaper behind * applications that would like to sit on top of it. This service object @@ -90,7 +100,8 @@ public abstract class WallpaperService extends Service { private static final int DO_ATTACH = 10; private static final int DO_DETACH = 20; private static final int DO_SET_DESIRED_SIZE = 30; - + private static final int DO_SET_DISPLAY_PADDING = 40; + private static final int MSG_UPDATE_SURFACE = 10000; private static final int MSG_VISIBILITY_CHANGED = 10010; private static final int MSG_WALLPAPER_OFFSETS = 10020; @@ -150,13 +161,23 @@ public abstract class WallpaperService extends Service { WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; int mCurWindowFlags = mWindowFlags; int mCurWindowPrivateFlags = mWindowPrivateFlags; + TypedValue mOutsetBottom; final Rect mVisibleInsets = new Rect(); final Rect mWinFrame = new Rect(); final Rect mOverscanInsets = new Rect(); final Rect mContentInsets = new Rect(); final Rect mStableInsets = new Rect(); + final Rect mDispatchedOverscanInsets = new Rect(); + final Rect mDispatchedContentInsets = new Rect(); + final Rect mDispatchedStableInsets = new Rect(); + final Rect mFinalSystemInsets = new Rect(); + final Rect mFinalStableInsets = new Rect(); final Configuration mConfiguration = new Configuration(); - + + private boolean mIsEmulator; + private boolean mIsCircularEmulator; + private boolean mWindowIsRound; + final WindowManager.LayoutParams mLayout = new WindowManager.LayoutParams(); IWindowSession mSession; @@ -406,7 +427,7 @@ public abstract class WallpaperService extends Service { */ public void onCreate(SurfaceHolder surfaceHolder) { } - + /** * Called right before the engine is going away. After this the * surface will be destroyed and this Engine object is no longer @@ -414,7 +435,7 @@ public abstract class WallpaperService extends Service { */ public void onDestroy() { } - + /** * Called to inform you of the wallpaper becoming visible or * hidden. <em>It is very important that a wallpaper only use @@ -422,7 +443,17 @@ public abstract class WallpaperService extends Service { */ public void onVisibilityChanged(boolean visible) { } - + + /** + * Called with the current insets that are in effect for the wallpaper. + * This gives you the part of the overall wallpaper surface that will + * generally be visible to the user (ignoring position offsets applied to it). + * + * @param insets Insets to apply. + */ + public void onApplyWindowInsets(WindowInsets insets) { + } + /** * Called as the user performs touch-screen interaction with the * window that is currently showing this wallpaper. Note that the @@ -432,7 +463,7 @@ public abstract class WallpaperService extends Service { */ public void onTouchEvent(MotionEvent event) { } - + /** * Called to inform you of the wallpaper's offsets changing * within its contain, corresponding to the container's @@ -443,7 +474,7 @@ public abstract class WallpaperService extends Service { float xOffsetStep, float yOffsetStep, int xPixelOffset, int yPixelOffset) { } - + /** * Process a command that was sent to the wallpaper with * {@link WallpaperManager#sendWallpaperCommand}. @@ -465,14 +496,14 @@ public abstract class WallpaperService extends Service { Bundle extras, boolean resultRequested) { return null; } - + /** * Called when an application has changed the desired virtual size of * the wallpaper. */ public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) { } - + /** * Convenience for {@link SurfaceHolder.Callback#surfaceChanged * SurfaceHolder.Callback.surfaceChanged()}. @@ -561,16 +592,20 @@ public abstract class WallpaperService extends Service { if (mDestroyed) { Log.w(TAG, "Ignoring updateSurface: destroyed"); } - + + boolean fixedSize = false; int myWidth = mSurfaceHolder.getRequestedWidth(); if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; + else fixedSize = true; int myHeight = mSurfaceHolder.getRequestedHeight(); if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; - + else fixedSize = true; + final boolean creating = !mCreated; final boolean surfaceCreating = !mSurfaceCreated; final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; + boolean insetsChanged = !mCreated; final boolean typeChanged = mType != mSurfaceHolder.getRequestedType(); final boolean flagsChanged = mCurWindowFlags != mWindowFlags || mCurWindowPrivateFlags != mWindowPrivateFlags; @@ -607,6 +642,32 @@ public abstract class WallpaperService extends Service { mLayout.token = mWindowToken; if (!mCreated) { + // Retrieve watch round and outset info + final WindowManager windowService = (WindowManager)getSystemService( + Context.WINDOW_SERVICE); + TypedArray windowStyle = obtainStyledAttributes( + com.android.internal.R.styleable.Window); + final Display display = windowService.getDefaultDisplay(); + final boolean shouldUseBottomOutset = + display.getDisplayId() == Display.DEFAULT_DISPLAY; + if (shouldUseBottomOutset && windowStyle.hasValue( + R.styleable.Window_windowOutsetBottom)) { + if (mOutsetBottom == null) mOutsetBottom = new TypedValue(); + windowStyle.getValue(R.styleable.Window_windowOutsetBottom, + mOutsetBottom); + } else { + mOutsetBottom = null; + } + mWindowIsRound = getResources().getBoolean( + com.android.internal.R.bool.config_windowIsRound); + windowStyle.recycle(); + + // detect emulator + mIsEmulator = Build.HARDWARE.contains("goldfish"); + mIsCircularEmulator = SystemProperties.getBoolean( + ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false); + + // Add window mLayout.type = mIWallpaperEngine.mWindowType; mLayout.gravity = Gravity.START|Gravity.TOP; mLayout.setTitle(WallpaperService.this.getClass().getName()); @@ -627,6 +688,11 @@ public abstract class WallpaperService extends Service { mSurfaceHolder.mSurfaceLock.lock(); mDrawingAllowed = true; + if (!fixedSize) { + mLayout.surfaceInsets.set(mIWallpaperEngine.mDisplayPadding); + } else { + mLayout.surfaceInsets.set(0, 0, 0, 0); + } final int relayoutResult = mSession.relayout( mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, @@ -636,16 +702,39 @@ public abstract class WallpaperService extends Service { + ", frame=" + mWinFrame); int w = mWinFrame.width(); + int h = mWinFrame.height(); + + if (!fixedSize) { + final Rect padding = mIWallpaperEngine.mDisplayPadding; + w += padding.left + padding.right; + h += padding.top + padding.bottom; + mOverscanInsets.left += padding.left; + mOverscanInsets.top += padding.top; + mOverscanInsets.right += padding.right; + mOverscanInsets.bottom += padding.bottom; + mContentInsets.left += padding.left; + mContentInsets.top += padding.top; + mContentInsets.right += padding.right; + mContentInsets.bottom += padding.bottom; + mStableInsets.left += padding.left; + mStableInsets.top += padding.top; + mStableInsets.right += padding.right; + mStableInsets.bottom += padding.bottom; + } + if (mCurWidth != w) { sizeChanged = true; mCurWidth = w; } - int h = mWinFrame.height(); if (mCurHeight != h) { sizeChanged = true; mCurHeight = h; } + insetsChanged |= !mDispatchedOverscanInsets.equals(mOverscanInsets); + insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets); + insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets); + mSurfaceHolder.setSurfaceFrameSize(w, h); mSurfaceHolder.mSurfaceLock.unlock(); @@ -702,6 +791,25 @@ public abstract class WallpaperService extends Service { } } + if (insetsChanged) { + mDispatchedOverscanInsets.set(mOverscanInsets); + mDispatchedContentInsets.set(mContentInsets); + mDispatchedStableInsets.set(mStableInsets); + final boolean isRound = (mIsEmulator && mIsCircularEmulator) + || mWindowIsRound; + mFinalSystemInsets.set(mDispatchedOverscanInsets); + mFinalStableInsets.set(mDispatchedStableInsets); + if (mOutsetBottom != null) { + final DisplayMetrics metrics = getResources().getDisplayMetrics(); + mFinalSystemInsets.bottom = + ( (int) mOutsetBottom.getDimension(metrics) ) + + mIWallpaperEngine.mDisplayPadding.bottom; + } + WindowInsets insets = new WindowInsets(mFinalSystemInsets, + null, mFinalStableInsets, isRound); + onApplyWindowInsets(insets); + } + if (redrawNeeded) { onSurfaceRedrawNeeded(mSurfaceHolder); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); @@ -781,7 +889,7 @@ public abstract class WallpaperService extends Service { mReportedVisible = false; updateSurface(false, false, false); } - + void doDesiredSizeChanged(int desiredWidth, int desiredHeight) { if (!mDestroyed) { if (DEBUG) Log.v(TAG, "onDesiredSizeChanged(" @@ -792,14 +900,24 @@ public abstract class WallpaperService extends Service { doOffsetsChanged(true); } } - + + void doDisplayPaddingChanged(Rect padding) { + if (!mDestroyed) { + if (DEBUG) Log.v(TAG, "onDisplayPaddingChanged(" + padding + "): " + this); + if (!mIWallpaperEngine.mDisplayPadding.equals(padding)) { + mIWallpaperEngine.mDisplayPadding.set(padding); + updateSurface(true, false, false); + } + } + } + void doVisibilityChanged(boolean visible) { if (!mDestroyed) { mVisible = visible; reportVisibility(); } } - + void reportVisibility() { if (!mDestroyed) { boolean visible = mVisible && mScreenOn; @@ -956,12 +1074,13 @@ public abstract class WallpaperService extends Service { boolean mShownReported; int mReqWidth; int mReqHeight; - + final Rect mDisplayPadding = new Rect(); + Engine mEngine; - + IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, - int windowType, boolean isPreview, int reqWidth, int reqHeight) { + int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); mConnection = conn; mWindowToken = windowToken; @@ -969,16 +1088,22 @@ public abstract class WallpaperService extends Service { mIsPreview = isPreview; mReqWidth = reqWidth; mReqHeight = reqHeight; + mDisplayPadding.set(padding); Message msg = mCaller.obtainMessage(DO_ATTACH); mCaller.sendMessage(msg); } - + public void setDesiredSize(int width, int height) { Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height); mCaller.sendMessage(msg); } - + + public void setDisplayPadding(Rect padding) { + Message msg = mCaller.obtainMessageO(DO_SET_DISPLAY_PADDING, padding); + mCaller.sendMessage(msg); + } + public void setVisibility(boolean visible) { Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED, visible ? 1 : 0); @@ -1041,6 +1166,9 @@ public abstract class WallpaperService extends Service { mEngine.doDesiredSizeChanged(message.arg1, message.arg2); return; } + case DO_SET_DISPLAY_PADDING: { + mEngine.doDisplayPaddingChanged((Rect) message.obj); + } case MSG_UPDATE_SURFACE: mEngine.updateSurface(true, false, false); break; @@ -1102,9 +1230,9 @@ public abstract class WallpaperService extends Service { @Override public void attach(IWallpaperConnection conn, IBinder windowToken, - int windowType, boolean isPreview, int reqWidth, int reqHeight) { + int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding) { new IWallpaperEngineWrapper(mTarget, conn, windowToken, - windowType, isPreview, reqWidth, reqHeight); + windowType, isPreview, reqWidth, reqHeight, padding); } } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 7245975..a4b6e92 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -578,7 +578,7 @@ public class TextToSpeech { * * @deprecated Starting from API level 20, to select network synthesis, call * ({@link TextToSpeech#getVoices()}, find a suitable network voice - * ({@link Voice#getRequiresNetworkConnection()}) and pass it + * ({@link Voice#isNetworkConnectionRequired()}) and pass it * to {@link TextToSpeech#setVoice(Voice)}). */ @Deprecated @@ -596,7 +596,7 @@ public class TextToSpeech { * @deprecated Starting from API level 20, to select embedded synthesis, call * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice - * ({@link Voice#getRequiresNetworkConnection()}) and pass it + * ({@link Voice#isNetworkConnectionRequired()}) and pass it * to {@link TextToSpeech#setVoice(Voice)}). */ @Deprecated @@ -957,20 +957,18 @@ public class TextToSpeech { * * @param text * The string of text. Example: <code>"south_south_east"</code> - * @param filename - * The full path to the sound file (for example: - * "/sdcard/mysounds/hello.wav") + * @param file + * File object pointing to the sound file. * * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. */ - public int addSpeech(CharSequence text, String filename) { + public int addSpeech(CharSequence text, File file) { synchronized (mStartLock) { - mUtterances.put(text, Uri.parse(filename)); + mUtterances.put(text, Uri.fromFile(file)); return SUCCESS; } } - /** * Adds a mapping between a string of text and a sound resource in a * package. Use this to add custom earcons. @@ -1017,7 +1015,11 @@ public class TextToSpeech { * "/sdcard/mysounds/tick.wav") * * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + * + * @deprecated As of API level 20, replaced by + * {@link #addEarcon(String, File)}. */ + @Deprecated public int addEarcon(String earcon, String filename) { synchronized(mStartLock) { mEarcons.put(earcon, Uri.parse(filename)); @@ -1025,6 +1027,27 @@ public class TextToSpeech { } } + /** + * Adds a mapping between a string of text and a sound file. + * Use this to add custom earcons. + * + * @see #playEarcon(String, int, HashMap) + * + * @param earcon + * The name of the earcon. + * Example: <code>"[tick]"</code> + * @param file + * File object pointing to the sound file. + * + * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + */ + public int addEarcon(String earcon, File file) { + synchronized(mStartLock) { + mEarcons.put(earcon, Uri.fromFile(file)); + return SUCCESS; + } + } + private Uri makeResourceUri(String packageName, int resourceId) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) @@ -1061,7 +1084,7 @@ public class TextToSpeech { */ public int speak(final CharSequence text, final int queueMode, - final HashMap<String, String> params, + final Bundle params, final String utteranceId) { return runAction(new Action<Integer>() { @Override @@ -1103,11 +1126,11 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. * @deprecated As of API level 20, replaced by - * {@link #speak(CharSequence, int, HashMap, String)}. + * {@link #speak(CharSequence, int, Bundle, String)}. */ @Deprecated public int speak(final String text, final int queueMode, final HashMap<String, String> params) { - return speak(text, queueMode, params, + return speak(text, queueMode, convertParamsHashMaptoBundle(params), params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } @@ -1135,7 +1158,7 @@ public class TextToSpeech { * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. */ public int playEarcon(final String earcon, final int queueMode, - final HashMap<String, String> params, final String utteranceId) { + final Bundle params, final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { @@ -1173,12 +1196,12 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. * @deprecated As of API level 20, replaced by - * {@link #playEarcon(String, int, HashMap, String)}. + * {@link #playEarcon(String, int, Bundle, String)}. */ @Deprecated public int playEarcon(final String earcon, final int queueMode, final HashMap<String, String> params) { - return playEarcon(earcon, queueMode, params, + return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params), params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } @@ -1757,22 +1780,20 @@ public class TextToSpeech { * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. - * @param filename Absolute file filename to write the generated audio data to.It should be - * something like "/sdcard/myappsounds/mysound.wav". + * @param file File to write the generated audio data to. * @param utteranceId An unique identifier for this request. * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. */ - public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params, - final String filename, final String utteranceId) { + public int synthesizeToFile(final CharSequence text, final Bundle params, + final File file, final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { ParcelFileDescriptor fileDescriptor; int returnValue; try { - File file = new File(filename); if(file.exists() && !file.canWrite()) { - Log.e(TAG, "Can't write to " + filename); + Log.e(TAG, "Can't write to " + file); return ERROR; } fileDescriptor = ParcelFileDescriptor.open(file, @@ -1784,10 +1805,10 @@ public class TextToSpeech { fileDescriptor.close(); return returnValue; } catch (FileNotFoundException e) { - Log.e(TAG, "Opening file " + filename + " failed", e); + Log.e(TAG, "Opening file " + file + " failed", e); return ERROR; } catch (IOException e) { - Log.e(TAG, "Closing file " + filename + " failed", e); + Log.e(TAG, "Closing file " + file + " failed", e); return ERROR; } } @@ -1817,16 +1838,18 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. * @deprecated As of API level 20, replaced by - * {@link #synthesizeToFile(CharSequence, HashMap, String, String)}. + * {@link #synthesizeToFile(CharSequence, Bundle, File, String)}. */ + @Deprecated public int synthesizeToFile(final String text, final HashMap<String, String> params, final String filename) { - return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + return synthesizeToFile(text, convertParamsHashMaptoBundle(params), + new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } - private Bundle getParams(HashMap<String, String> params) { + private Bundle convertParamsHashMaptoBundle(HashMap<String, String> params) { if (params != null && !params.isEmpty()) { - Bundle bundle = new Bundle(mParams); + Bundle bundle = new Bundle(); copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID); copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); @@ -1852,11 +1875,85 @@ public class TextToSpeech { } return bundle; + } + return null; + } + + private Bundle getParams(Bundle params) { + if (params != null && !params.isEmpty()) { + Bundle bundle = new Bundle(mParams); + bundle.putAll(params); + + verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM); + verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID); + verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID); + verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME); + verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN); + + // Copy feature strings defined by the framework. + verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS); + verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS); + verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS); + verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT); + + return bundle; } else { return mParams; } } + private static boolean verifyIntegerBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Integer || + bundle.get(key) instanceof Long)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be an Integer or a Long"); + return false; + } + } + return true; + } + + private static boolean verifyStringBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof String)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a String"); + return false; + } + } + return true; + } + + private static boolean verifyBooleanBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Boolean || + bundle.get(key) instanceof String)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a Boolean or String"); + return false; + } + } + return true; + } + + + private static boolean verifyFloatBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Float || + bundle.get(key) instanceof Double)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a Float or a Double"); + return false; + } + } + return true; + } + private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) { String value = params.get(key); if (value != null) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 4fea109..d00a433 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -84,7 +84,7 @@ import java.util.Set; * the following methods: * <ul> * <li>{@link #onGetVoices()}</li> - * <li>{@link #isValidVoiceName(String)}</li> + * <li>{@link #onIsValidVoiceName(String)}</li> * <li>{@link #onLoadVoice(String)}</li> * <li>{@link #onGetDefaultVoiceNameFor(String, String, String)}</li> * </ul> @@ -278,7 +278,7 @@ public abstract class TextToSpeechService extends Service { * * @return A list of voices supported. */ - protected List<Voice> onGetVoices() { + public List<Voice> onGetVoices() { // Enumerate all locales and check if they are available ArrayList<Voice> voices = new ArrayList<Voice>(); for (Locale locale : Locale.getAvailableLocales()) { @@ -335,7 +335,7 @@ public abstract class TextToSpeechService extends Service { } Locale properLocale = TtsEngines.normalizeTTSLocale(iso3Locale); String voiceName = properLocale.toLanguageTag(); - if (isValidVoiceName(voiceName) == TextToSpeech.SUCCESS) { + if (onIsValidVoiceName(voiceName) == TextToSpeech.SUCCESS) { return voiceName; } else { return null; @@ -357,7 +357,7 @@ public abstract class TextToSpeechService extends Service { * @param voiceName Name of the voice. * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. */ - protected int onLoadVoice(String voiceName) { + public int onLoadVoice(String voiceName) { Locale locale = Locale.forLanguageTag(voiceName); if (locale == null) { return TextToSpeech.ERROR; @@ -388,7 +388,7 @@ public abstract class TextToSpeechService extends Service { * @param voiceName Name of the voice. * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. */ - protected int isValidVoiceName(String voiceName) { + public int onIsValidVoiceName(String voiceName) { Locale locale = Locale.forLanguageTag(voiceName); if (locale == null) { return TextToSpeech.ERROR; @@ -1275,7 +1275,7 @@ public abstract class TextToSpeechService extends Service { if (!checkNonNull(voiceName)) { return TextToSpeech.ERROR; } - int retVal = isValidVoiceName(voiceName); + int retVal = onIsValidVoiceName(voiceName); if (retVal == TextToSpeech.SUCCESS) { SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(), diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java index a97141c..a1fa51d 100644 --- a/core/java/android/speech/tts/Voice.java +++ b/core/java/android/speech/tts/Voice.java @@ -162,7 +162,7 @@ public class Voice implements Parcelable { /** * @return Does the Voice require a network connection to work. */ - public boolean getRequiresNetworkConnection() { + public boolean isNetworkConnectionRequired() { return mRequiresNetworkConnection; } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 0d1b568..40bb6ec 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -1417,9 +1417,9 @@ public abstract class Transition implements Cloneable { } capturePropagationValues(values); if (start) { - addViewValues(mStartValues, view, values); + addViewValues(mStartValues, view, values, true); } else { - addViewValues(mEndValues, view, values); + addViewValues(mEndValues, view, values, true); } } } @@ -1460,7 +1460,7 @@ public abstract class Transition implements Cloneable { } static void addViewValues(TransitionValuesMaps transitionValuesMaps, - View view, TransitionValues transitionValues) { + View view, TransitionValues transitionValues, boolean setTransientState) { transitionValuesMaps.viewValues.put(view, transitionValues); int id = view.getId(); if (id >= 0) { @@ -1489,11 +1489,15 @@ public abstract class Transition implements Cloneable { // Duplicate item IDs: cannot match by item ID. View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId); if (alreadyMatched != null) { - alreadyMatched.setHasTransientState(false); + if (setTransientState) { + alreadyMatched.setHasTransientState(false); + } transitionValuesMaps.itemIdValues.put(itemId, null); } } else { - view.setHasTransientState(true); + if (setTransientState) { + view.setHasTransientState(true); + } transitionValuesMaps.itemIdValues.put(itemId, view); } } @@ -1560,9 +1564,9 @@ public abstract class Transition implements Cloneable { } capturePropagationValues(values); if (start) { - addViewValues(mStartValues, view, values); + addViewValues(mStartValues, view, values, true); } else { - addViewValues(mEndValues, view, values); + addViewValues(mEndValues, view, values, true); } } if (view instanceof ViewGroup) { diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index f6499ae..56db674 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -408,7 +408,7 @@ public class TransitionSet extends Transition { for (int i = 0; i < numValues; i++) { View view = values.viewValues.keyAt(i); if (isValidTarget(view)) { - addViewValues(included, view, values.viewValues.valueAt(i)); + addViewValues(included, view, values.viewValues.valueAt(i), false); } } return included; diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 0b02552..d648ca6 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -358,12 +358,16 @@ public abstract class Visibility extends Transition { overlayView = startView; } else if (startView.getParent() instanceof View) { View startParent = (View) startView.getParent(); - if (!isValidTarget(startParent)) { - if (startView.isAttachedToWindow()) { - overlayView = copyViewImage(startView); - } else { - overlayView = startView; - } + VisibilityInfo parentVisibilityInfo = null; + TransitionValues endParentValues = getMatchedTransitionValues(startParent, + true); + if (endParentValues != null) { + TransitionValues startParentValues = getTransitionValues(startParent, true); + parentVisibilityInfo = + getVisibilityChangeInfo(startParentValues, endParentValues); + } + if (parentVisibilityInfo == null || !parentVisibilityInfo.visibilityChange) { + overlayView = copyViewImage(startView); } else if (startParent.getParent() == null) { int id = startParent.getId(); if (id != View.NO_ID && sceneRoot.findViewById(id) != null diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index a10dda3..a283b91 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -19,7 +19,6 @@ package android.view; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -101,7 +100,7 @@ final class AccessibilityInteractionController { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); - message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; + message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID; message.arg1 = flags; SomeArgs args = SomeArgs.obtain(); @@ -176,7 +175,7 @@ final class AccessibilityInteractionController { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); - message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID; + message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID; message.arg1 = flags; message.arg2 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); @@ -261,7 +260,7 @@ final class AccessibilityInteractionController { IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { Message message = mHandler.obtainMessage(); - message.what = PrivateHandler.MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; + message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT; message.arg1 = flags; SomeArgs args = SomeArgs.obtain(); @@ -637,6 +636,95 @@ final class AccessibilityInteractionController { } } + public void computeClickPointInScreenClientThread(long accessibilityNodeId, + Region interactiveRegion, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, + long interrogatingTid, MagnificationSpec spec) { + Message message = mHandler.obtainMessage(); + message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN; + + SomeArgs args = SomeArgs.obtain(); + args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); + args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); + args.argi3 = interactionId; + args.arg1 = callback; + args.arg2 = spec; + args.arg3 = interactiveRegion; + + message.obj = args; + + // If the interrogation is performed by the same thread as the main UI + // thread in this process, set the message as a static reference so + // after this call completes the same thread but in the interrogating + // client can handle the message to generate the result. + if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) { + AccessibilityInteractionClient.getInstanceForThread( + interrogatingTid).setSameThreadMessage(message); + } else { + mHandler.sendMessage(message); + } + } + + private void computeClickPointInScreenUiThread(Message message) { + SomeArgs args = (SomeArgs) message.obj; + final int accessibilityViewId = args.argi1; + final int virtualDescendantId = args.argi2; + final int interactionId = args.argi3; + final IAccessibilityInteractionConnectionCallback callback = + (IAccessibilityInteractionConnectionCallback) args.arg1; + final MagnificationSpec spec = (MagnificationSpec) args.arg2; + final Region interactiveRegion = (Region) args.arg3; + args.recycle(); + + boolean succeeded = false; + Point point = mTempPoint; + try { + if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) { + return; + } + View target = null; + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + target = findViewByAccessibilityId(accessibilityViewId); + } else { + target = mViewRootImpl.mView; + } + if (target != null && isShown(target)) { + AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); + if (provider != null) { + // For virtual views just use the center of the bounds in screen. + AccessibilityNodeInfo node = null; + if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + node = provider.createAccessibilityNodeInfo(virtualDescendantId); + } else { + node = provider.createAccessibilityNodeInfo( + AccessibilityNodeProvider.HOST_VIEW_ID); + } + if (node != null) { + succeeded = true; + Rect boundsInScreen = mTempRect; + node.getBoundsInScreen(boundsInScreen); + point.set(boundsInScreen.centerX(), boundsInScreen.centerY()); + } + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) { + // For a real view, ask the view to compute the click point. + succeeded = target.computeClickPointInScreenForAccessibility( + interactiveRegion, point); + } + } + } finally { + try { + Point result = null; + if (succeeded) { + applyAppScaleAndMagnificationSpecIfNeeded(point, spec); + result = point; + } + callback.setComputeClickPointInScreenActionResult(result, interactionId); + } catch (RemoteException re) { + /* ignore - the other side will time out */ + } + } + } + private View findViewByAccessibilityId(int accessibilityId) { View root = mViewRootImpl.mView; if (root == null) { @@ -688,6 +776,26 @@ final class AccessibilityInteractionController { } } + private void applyAppScaleAndMagnificationSpecIfNeeded(Point point, + MagnificationSpec spec) { + final float applicationScale = mViewRootImpl.mAttachInfo.mApplicationScale; + if (!shouldApplyAppScaleAndMagnificationSpec(applicationScale, spec)) { + return; + } + + if (applicationScale != 1.0f) { + point.x *= applicationScale; + point.y *= applicationScale; + } + + if (spec != null) { + point.x *= spec.scale; + point.y *= spec.scale; + point.x += (int) spec.offsetX; + point.y += (int) spec.offsetY; + } + } + private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info, MagnificationSpec spec) { if (info == null) { @@ -1080,11 +1188,12 @@ final class AccessibilityInteractionController { private class PrivateHandler extends Handler { private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 1; - private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2; - private final static int MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID = 3; - private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 4; + private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID = 2; + private final static int MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID = 3; + private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4; private final static int MSG_FIND_FOCUS = 5; private final static int MSG_FOCUS_SEARCH = 6; + private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7; public PrivateHandler(Looper looper) { super(looper); @@ -1096,16 +1205,18 @@ final class AccessibilityInteractionController { switch (type) { case MSG_PERFORM_ACCESSIBILITY_ACTION: return "MSG_PERFORM_ACCESSIBILITY_ACTION"; - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: - return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; - case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: - return "MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID"; - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: - return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT"; + case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: + return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID"; + case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: + return "MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID"; + case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: + return "MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT"; case MSG_FIND_FOCUS: return "MSG_FIND_FOCUS"; case MSG_FOCUS_SEARCH: return "MSG_FOCUS_SEARCH"; + case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: + return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN"; default: throw new IllegalArgumentException("Unknown message type: " + type); } @@ -1115,16 +1226,16 @@ final class AccessibilityInteractionController { public void handleMessage(Message message) { final int type = message.what; switch (type) { - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { + case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID: { findAccessibilityNodeInfoByAccessibilityIdUiThread(message); } break; case MSG_PERFORM_ACCESSIBILITY_ACTION: { perfromAccessibilityActionUiThread(message); } break; - case MSG_FIND_ACCESSIBLITY_NODE_INFOS_BY_VIEW_ID: { + case MSG_FIND_ACCESSIBILITY_NODE_INFOS_BY_VIEW_ID: { findAccessibilityNodeInfosByViewIdUiThread(message); } break; - case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: { + case MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT: { findAccessibilityNodeInfosByTextUiThread(message); } break; case MSG_FIND_FOCUS: { @@ -1133,6 +1244,9 @@ final class AccessibilityInteractionController { case MSG_FOCUS_SEARCH: { focusSearchUiThread(message); } break; + case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: { + computeClickPointInScreenUiThread(message); + } break; default: throw new IllegalArgumentException("Unknown message type: " + type); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 5d6d998..6aa86c7 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -94,7 +94,8 @@ interface IWindowManager void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY, IRemoteCallback startedCallback, boolean scaleUp); void overridePendingAppTransitionAspectScaledThumb(in Bitmap srcThumb, int startX, - int startY, IRemoteCallback startedCallback, boolean scaleUp); + int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback, + boolean scaleUp); void executeAppTransition(); void setAppStartingWindow(IBinder token, String pkg, int theme, in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 0f3f182..037ed28 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -177,6 +177,11 @@ interface IWindowSession { void wallpaperOffsetsComplete(IBinder window); + /** + * Apply a raw offset to the wallpaper service when shown behind this window. + */ + void setWallpaperDisplayOffset(IBinder windowToken, int x, int y); + Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, int z, in Bundle extras, boolean sync); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 770e78c..82c5425 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -35,6 +35,8 @@ import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; @@ -5731,6 +5733,136 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Computes a point on which a sequence of a down/up event can be sent to + * trigger clicking this view. This method is for the exclusive use by the + * accessibility layer to determine where to send a click event in explore + * by touch mode. + * + * @param interactiveRegion The interactive portion of this window. + * @param outPoint The point to populate. + * @return True of such a point exists. + */ + boolean computeClickPointInScreenForAccessibility(Region interactiveRegion, + Point outPoint) { + // Since the interactive portion of the view is a region but as a view + // may have a transformation matrix which cannot be applied to a + // region we compute the view bounds rectangle and all interactive + // predecessor's and sibling's (siblings of predecessors included) + // rectangles that intersect the view bounds. At the + // end if the view was partially covered by another interactive + // view we compute the view's interactive region and pick a point + // on its boundary path as regions do not offer APIs to get inner + // points. Note that the the code is optimized to fail early and + // avoid unnecessary allocations plus computations. + + // The current approach has edge cases that may produce false + // positives or false negatives. For example, a portion of the + // view may be covered by an interactive descendant of a + // predecessor, which we do not compute. Also a view may be handling + // raw touch events instead registering click listeners, which + // we cannot compute. Despite these limitations this approach will + // work most of the time and it is a huge improvement over just + // blindly sending the down and up events in the center of the + // view. + + // Cannot click on an unattached view. + if (mAttachInfo == null) { + return false; + } + + // Attached to an invisible window means this view is not visible. + if (mAttachInfo.mWindowVisibility != View.VISIBLE) { + return false; + } + + RectF bounds = mAttachInfo.mTmpTransformRect; + bounds.set(0, 0, getWidth(), getHeight()); + List<RectF> intersections = mAttachInfo.mTmpRectList; + intersections.clear(); + + if (mParent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) mParent; + if (!parentGroup.translateBoundsAndIntersectionsInWindowCoordinates( + this, bounds, intersections)) { + intersections.clear(); + return false; + } + } + + // Take into account the window location. + final int dx = mAttachInfo.mWindowLeft; + final int dy = mAttachInfo.mWindowTop; + bounds.offset(dx, dy); + offsetRects(intersections, dx, dy); + + if (intersections.isEmpty() && interactiveRegion == null) { + outPoint.set((int) bounds.centerX(), (int) bounds.centerY()); + } else { + // This view is partially covered by other views, then compute + // the not covered region and pick a point on its boundary. + Region region = new Region(); + region.set((int) bounds.left, (int) bounds.top, + (int) bounds.right, (int) bounds.bottom); + + final int intersectionCount = intersections.size(); + for (int i = intersectionCount - 1; i >= 0; i--) { + RectF intersection = intersections.remove(i); + region.op((int) intersection.left, (int) intersection.top, + (int) intersection.right, (int) intersection.bottom, + Region.Op.DIFFERENCE); + } + + // If the view is completely covered, done. + if (region.isEmpty()) { + return false; + } + + // Take into account the interactive portion of the window + // as the rest is covered by other windows. If no such a region + // then the whole window is interactive. + if (interactiveRegion != null) { + region.op(interactiveRegion, Region.Op.INTERSECT); + } + + // If the view is completely covered, done. + if (region.isEmpty()) { + return false; + } + + // Try a shortcut here. + if (region.isRect()) { + Rect regionBounds = mAttachInfo.mTmpInvalRect; + region.getBounds(regionBounds); + outPoint.set(regionBounds.centerX(), regionBounds.centerY()); + return true; + } + + // Get the a point on the region boundary path. + Path path = region.getBoundaryPath(); + PathMeasure pathMeasure = new PathMeasure(path, false); + final float[] coordinates = mAttachInfo.mTmpTransformLocation; + + // Without loss of generality pick a point. + final float point = pathMeasure.getLength() * 0.01f; + if (!pathMeasure.getPosTan(point, coordinates, null)) { + return false; + } + + outPoint.set(Math.round(coordinates[0]), Math.round(coordinates[1])); + } + + return true; + } + + static void offsetRects(List<RectF> rects, float offsetX, float offsetY) { + final int rectCount = rects.size(); + for (int i = 0; i < rectCount; i++) { + RectF intersection = rects.get(i); + intersection.offset(offsetX, offsetY); + } + } + + /** * Returns the delegate for implementing accessibility support via * composition. For more details see {@link AccessibilityDelegate}. * @@ -20154,6 +20286,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final RectF mTmpTransformRect = new RectF(); /** + * Temporary for use in computing hit areas with transformed views + */ + final RectF mTmpTransformRect1 = new RectF(); + + /** + * Temporary list of rectanges. + */ + final List<RectF> mTmpRectList = new ArrayList<>(); + + /** * Temporary for use in transforming invalidation rect */ final Matrix mTmpMatrix = new Matrix(); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index c1e66de..4e1db90 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -771,6 +771,112 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Translates the given bounds and intersections from child coordinates to + * local coordinates. In case any interactive sibling of the calling child + * covers the latter, a new intersections is added to the intersection list. + * This method is for the exclusive use by the accessibility layer to compute + * a point where a sequence of down and up events would click on a view. + * + * @param child The child making the call. + * @param bounds The bounds to translate in child coordinates. + * @param intersections The intersections of interactive views covering the child. + * @return True if the bounds and intersections were computed, false otherwise. + */ + boolean translateBoundsAndIntersectionsInWindowCoordinates(View child, + RectF bounds, List<RectF> intersections) { + // Not attached, done. + if (mAttachInfo == null) { + return false; + } + + if (getAlpha() <= 0 || getTransitionAlpha() <= 0 || + getVisibility() != VISIBLE) { + // Cannot click on a view with an invisible predecessor. + return false; + } + + // Compensate for the child transformation. + if (!child.hasIdentityMatrix()) { + Matrix matrix = child.getMatrix(); + matrix.mapRect(bounds); + final int intersectionCount = intersections.size(); + for (int i = 0; i < intersectionCount; i++) { + RectF intersection = intersections.get(i); + matrix.mapRect(intersection); + } + } + + // Translate the bounds from child to parent coordinates. + final int dx = child.mLeft - mScrollX; + final int dy = child.mTop - mScrollY; + bounds.offset(dx, dy); + offsetRects(intersections, dx, dy); + + // If the bounds do not intersect our bounds, done. + if (!bounds.intersects(0, 0, getWidth(), getHeight())) { + return false; + } + + // Check whether any clickable siblings cover the child + // view and if so keep track of the intersections. Also + // respect Z ordering when iterating over children. + ArrayList<View> orderedList = buildOrderedChildList(); + final boolean useCustomOrder = orderedList == null + && isChildrenDrawingOrderEnabled(); + + final int childCount = mChildrenCount; + for (int i = childCount - 1; i >= 0; i--) { + final int childIndex = useCustomOrder + ? getChildDrawingOrder(childCount, i) : i; + final View sibling = (orderedList == null) + ? mChildren[childIndex] : orderedList.get(childIndex); + + // We care only about siblings over the child. + if (sibling == child) { + break; + } + + // If sibling is not interactive we do not care. + if (!sibling.isClickable() && !sibling.isLongClickable()) { + continue; + } + + // Compute the sibling bounds in its coordinates. + RectF siblingBounds = mAttachInfo.mTmpTransformRect1; + siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight()); + + // Take into account the sibling transformation matrix. + if (!sibling.hasIdentityMatrix()) { + sibling.getMatrix().mapRect(siblingBounds); + } + + // Offset the sibling to our coordinates. + final int siblingDx = sibling.mLeft - mScrollX; + final int siblingDy = sibling.mTop - mScrollY; + siblingBounds.offset(siblingDx, siblingDy); + + // Compute the intersection between the child and the sibling. + if (siblingBounds.intersect(bounds)) { + // If an interactive sibling completely covers the child, done. + if (siblingBounds.equals(bounds)) { + return false; + } + // Keep track of the intersection rectangle. + RectF intersection = new RectF(siblingBounds); + intersections.add(intersection); + } + } + + if (mParent instanceof ViewGroup) { + ViewGroup parentGroup = (ViewGroup) mParent; + return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates( + this, bounds, intersections); + } + + return true; + } + + /** * Called when a child view has changed whether or not it is tracking transient state. */ public void childHasTransientStateChanged(View child, boolean childHasTransientState) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4299e2e..43ab4ef 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -121,7 +121,7 @@ public final class ViewRootImpl implements ViewParent, private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media"; // property used by emulator to determine display shape - private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; + public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; /** * Maximum time we allow the user to roll the trackball enough to generate @@ -6679,12 +6679,12 @@ public final class ViewRootImpl implements ViewParent, public void performAccessibilityAction(long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, - int interogatingPid, long interrogatingTid) { + int interrogatingPid, long interrogatingTid) { ViewRootImpl viewRootImpl = mViewRootImpl.get(); if (viewRootImpl != null && viewRootImpl.mView != null) { viewRootImpl.getAccessibilityInteractionController() .performAccessibilityActionClientThread(accessibilityNodeId, action, arguments, - interactionId, callback, flags, interogatingPid, interrogatingTid); + interactionId, callback, flags, interrogatingPid, interrogatingTid); } else { // We cannot make the call and notify the caller so it does not wait. try { @@ -6696,6 +6696,26 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void computeClickPointInScreen(long accessibilityNodeId, Region interactiveRegion, + int interactionId, IAccessibilityInteractionConnectionCallback callback, + int interrogatingPid, long interrogatingTid, MagnificationSpec spec) { + ViewRootImpl viewRootImpl = mViewRootImpl.get(); + if (viewRootImpl != null && viewRootImpl.mView != null) { + viewRootImpl.getAccessibilityInteractionController() + .computeClickPointInScreenClientThread(accessibilityNodeId, + interactiveRegion, interactionId, callback, interrogatingPid, + interrogatingTid, spec); + } else { + // We cannot make the call and notify the caller so it does not wait. + try { + callback.setComputeClickPointInScreenActionResult(null, interactionId); + } catch (RemoteException re) { + /* best effort - ignore */ + } + } + } + + @Override public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId, String viewId, Region interactiveRegion, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 571a8f0..24c3c1a 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -378,35 +378,75 @@ public final class WindowInsets { } /** - * @hide + * Returns the top stable inset in pixels. + * + * <p>The stable inset represents the area of a full-screen window that <b>may</b> be + * partially or fully obscured by the system UI elements. This value does not change + * based on the visibility state of those elements; for example, if the status bar is + * normally shown, but temporarily hidden, the stable inset will still provide the inset + * associated with the status bar being shown.</p> + * + * @return The top stable inset */ public int getStableInsetTop() { return mStableInsets.top; } /** - * @hide + * Returns the left stable inset in pixels. + * + * <p>The stable inset represents the area of a full-screen window that <b>may</b> be + * partially or fully obscured by the system UI elements. This value does not change + * based on the visibility state of those elements; for example, if the status bar is + * normally shown, but temporarily hidden, the stable inset will still provide the inset + * associated with the status bar being shown.</p> + * + * @return The left stable inset */ public int getStableInsetLeft() { return mStableInsets.left; } /** - * @hide + * Returns the right stable inset in pixels. + * + * <p>The stable inset represents the area of a full-screen window that <b>may</b> be + * partially or fully obscured by the system UI elements. This value does not change + * based on the visibility state of those elements; for example, if the status bar is + * normally shown, but temporarily hidden, the stable inset will still provide the inset + * associated with the status bar being shown.</p> + * + * @return The right stable inset */ public int getStableInsetRight() { return mStableInsets.right; } /** - * @hide + * Returns the bottom stable inset in pixels. + * + * <p>The stable inset represents the area of a full-screen window that <b>may</b> be + * partially or fully obscured by the system UI elements. This value does not change + * based on the visibility state of those elements; for example, if the status bar is + * normally shown, but temporarily hidden, the stable inset will still provide the inset + * associated with the status bar being shown.</p> + * + * @return The bottom stable inset */ public int getStableInsetBottom() { return mStableInsets.bottom; } /** - * @hide + * Returns true if this WindowInsets has nonzero stable insets. + * + * <p>The stable inset represents the area of a full-screen window that <b>may</b> be + * partially or fully obscured by the system UI elements. This value does not change + * based on the visibility state of those elements; for example, if the status bar is + * normally shown, but temporarily hidden, the stable inset will still provide the inset + * associated with the status bar being shown.</p> + * + * @return true if any of the stable inset values are nonzero */ public boolean hasStableInsets() { return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0 @@ -414,7 +454,9 @@ public final class WindowInsets { } /** - * @hide + * Returns a copy of this WindowInsets with the stable insets fully consumed. + * + * @return A modified copy of this WindowInsets */ public WindowInsets consumeStableInsets() { final WindowInsets result = new WindowInsets(this); diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index db78ec5..374f7e0 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -17,6 +17,7 @@ package android.view.accessibility; import android.accessibilityservice.IAccessibilityServiceConnection; +import android.graphics.Point; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -98,6 +99,8 @@ public final class AccessibilityInteractionClient private boolean mPerformAccessibilityActionResult; + private Point mComputeClickPointResult; + private Message mSameThreadMessage; private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = @@ -519,6 +522,43 @@ public final class AccessibilityInteractionClient return false; } + /** + * Computes a point in screen coordinates where sending a down/up events would + * perform a click on an {@link AccessibilityNodeInfo}. + * + * @param connectionId The id of a connection for interacting with the system. + * @param accessibilityWindowId A unique window id. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} + * to query the currently active window. + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. + * @return Point the click point of null if no such point. + */ + public Point computeClickPointInScreen(int connectionId, int accessibilityWindowId, + long accessibilityNodeId) { + try { + IAccessibilityServiceConnection connection = getConnection(connectionId); + if (connection != null) { + final int interactionId = mInteractionIdCounter.getAndIncrement(); + final boolean success = connection.computeClickPointInScreen( + accessibilityWindowId, accessibilityNodeId, + interactionId, this, Thread.currentThread().getId()); + if (success) { + return getComputeClickPointInScreenResultAndClear(interactionId); + } + } else { + if (DEBUG) { + Log.w(LOG_TAG, "No connection for connection id: " + connectionId); + } + } + } catch (RemoteException re) { + Log.w(LOG_TAG, "Error while calling remote computeClickPointInScreen", re); + } + return null; + } + public void clearCache() { sAccessibilityCache.clear(); } @@ -634,6 +674,34 @@ public final class AccessibilityInteractionClient } /** + * Gets the result of a request to compute a point in screen for clicking on a node. + * + * @param interactionId The interaction id to match the result with the request. + * @return The point or null if no such point. + */ + private Point getComputeClickPointInScreenResultAndClear(int interactionId) { + synchronized (mInstanceLock) { + final boolean success = waitForResultTimedLocked(interactionId); + Point result = success ? mComputeClickPointResult : null; + clearResultLocked(); + return result; + } + } + + /** + * {@inheritDoc} + */ + public void setComputeClickPointInScreenActionResult(Point point, int interactionId) { + synchronized (mInstanceLock) { + if (interactionId > mInteractionId) { + mComputeClickPointResult = point; + mInteractionId = interactionId; + } + mInstanceLock.notifyAll(); + } + } + + /** * Clears the result state. */ private void clearResultLocked() { @@ -641,6 +709,7 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfoResult = null; mFindAccessibilityNodeInfosResult = null; mPerformAccessibilityActionResult = false; + mComputeClickPointResult = null; } /** diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl index faf7789..66a3f46 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl @@ -17,6 +17,7 @@ package android.view.accessibility; import android.graphics.Region; +import android.graphics.Point; import android.os.Bundle; import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; @@ -53,4 +54,8 @@ oneway interface IAccessibilityInteractionConnection { void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid, long interrogatingTid); + + void computeClickPointInScreen(long accessibilityNodeId, in Region bounds, int interactionId, + IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, + long interrogatingTid, in MagnificationSpec spec); } diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl index c1a3ab7..f480216 100644 --- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl +++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl @@ -16,6 +16,7 @@ package android.view.accessibility; +import android.graphics.Point; import android.view.accessibility.AccessibilityNodeInfo; import java.util.List; @@ -51,4 +52,12 @@ oneway interface IAccessibilityInteractionConnectionCallback { * @param interactionId The interaction id to match the result with the request. */ void setPerformAccessibilityActionResult(boolean succeeded, int interactionId); + + /** + * Sets the result of a request to compute a point for clicking in a view. + * + * @param point The point of null if no such point. + * @param interactionId The interaction id to match the result with the request. + */ + void setComputeClickPointInScreenActionResult(in Point point, int interactionId); } diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index fe0f5b9..600fffe 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -35,9 +35,21 @@ import java.util.Objects; * actually inserted.</p> */ public final class CursorAnchorInfo implements Parcelable { + /** + * The index of the first character of the selected text (inclusive). {@code -1} when there is + * no text selection. + */ private final int mSelectionStart; + /** + * The index of the first character of the selected text (exclusive). {@code -1} when there is + * no text selection. + */ private final int mSelectionEnd; + /** + * The index of the first character of the composing text (inclusive). {@code -1} when there is + * no composing text. + */ private final int mComposingTextStart; /** * The text, tracked as a composing region. @@ -82,7 +94,7 @@ public final class CursorAnchorInfo implements Parcelable { * Java chars, in the local coordinates that will be transformed with the transformation matrix * when rendered on the screen. */ - private final SparseRectFArray mCharacterRects; + private final SparseRectFArray mCharacterBoundsArray; /** * Transformation matrix that is applied to any positional information of this class to @@ -91,18 +103,24 @@ public final class CursorAnchorInfo implements Parcelable { private final Matrix mMatrix; /** - * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the + * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the * insertion marker or character bounds have at least one visible region. */ public static final int FLAG_HAS_VISIBLE_REGION = 0x01; /** - * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterRectFlags(int)}: the + * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the * insertion marker or character bounds have at least one invisible (clipped) region. */ public static final int FLAG_HAS_INVISIBLE_REGION = 0x02; /** + * Flag for {@link #getInsertionMarkerFlags()} and {@link #getCharacterBoundsFlags(int)}: the + * insertion marker or character bounds is placed at right-to-left (RTL) character. + */ + public static final int FLAG_IS_RTL = 0x04; + + /** * @removed */ public static final int CHARACTER_RECT_TYPE_MASK = 0x0f; @@ -144,7 +162,7 @@ public final class CursorAnchorInfo implements Parcelable { mInsertionMarkerTop = source.readFloat(); mInsertionMarkerBaseline = source.readFloat(); mInsertionMarkerBottom = source.readFloat(); - mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader()); + mCharacterBoundsArray = source.readParcelable(SparseRectFArray.class.getClassLoader()); mMatrix = new Matrix(); mMatrix.setValues(source.createFloatArray()); } @@ -166,7 +184,7 @@ public final class CursorAnchorInfo implements Parcelable { dest.writeFloat(mInsertionMarkerTop); dest.writeFloat(mInsertionMarkerBaseline); dest.writeFloat(mInsertionMarkerBottom); - dest.writeParcelable(mCharacterRects, flags); + dest.writeParcelable(mCharacterBoundsArray, flags); final float[] matrixArray = new float[9]; mMatrix.getValues(matrixArray); dest.writeFloatArray(matrixArray); @@ -174,7 +192,6 @@ public final class CursorAnchorInfo implements Parcelable { @Override public int hashCode(){ - // TODO: Improve the hash function. final float floatHash = mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline + mInsertionMarkerBottom; int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash); @@ -185,7 +202,7 @@ public final class CursorAnchorInfo implements Parcelable { hash *= 31; hash += Objects.hashCode(mComposingText); hash *= 31; - hash += Objects.hashCode(mCharacterRects); + hash += Objects.hashCode(mCharacterBoundsArray); hash *= 31; hash += Objects.hashCode(mMatrix); return hash; @@ -231,7 +248,7 @@ public final class CursorAnchorInfo implements Parcelable { || !areSameFloatImpl(mInsertionMarkerBottom, that.mInsertionMarkerBottom)) { return false; } - if (!Objects.equals(mCharacterRects, that.mCharacterRects)) { + if (!Objects.equals(mCharacterBoundsArray, that.mCharacterBoundsArray)) { return false; } if (!Objects.equals(mMatrix, that.mMatrix)) { @@ -250,7 +267,7 @@ public final class CursorAnchorInfo implements Parcelable { + " mInsertionMarkerTop=" + mInsertionMarkerTop + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline + " mInsertionMarkerBottom=" + mInsertionMarkerBottom - + " mCharacterRects=" + Objects.toString(mCharacterRects) + + " mCharacterBoundsArray=" + Objects.toString(mCharacterBoundsArray) + " mMatrix=" + Objects.toString(mMatrix) + "}"; } @@ -259,6 +276,19 @@ public final class CursorAnchorInfo implements Parcelable { * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe. */ public static final class Builder { + private int mSelectionStart = -1; + private int mSelectionEnd = -1; + private int mComposingTextStart = -1; + private CharSequence mComposingText = null; + private float mInsertionMarkerHorizontal = Float.NaN; + private float mInsertionMarkerTop = Float.NaN; + private float mInsertionMarkerBaseline = Float.NaN; + private float mInsertionMarkerBottom = Float.NaN; + private int mInsertionMarkerFlags = 0; + private SparseRectFArrayBuilder mCharacterBoundsArrayBuilder = null; + private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX); + private boolean mMatrixInitialized = false; + /** * Sets the text range of the selection. Calling this can be skipped if there is no * selection. @@ -268,8 +298,6 @@ public final class CursorAnchorInfo implements Parcelable { mSelectionEnd = newEnd; return this; } - private int mSelectionStart = -1; - private int mSelectionEnd = -1; /** * Sets the text range of the composing text. Calling this can be skipped if there is @@ -288,8 +316,6 @@ public final class CursorAnchorInfo implements Parcelable { } return this; } - private int mComposingTextStart = -1; - private CharSequence mComposingText = null; /** * @removed @@ -335,11 +361,33 @@ public final class CursorAnchorInfo implements Parcelable { mInsertionMarkerFlags = flags; return this; } - private float mInsertionMarkerHorizontal = Float.NaN; - private float mInsertionMarkerTop = Float.NaN; - private float mInsertionMarkerBaseline = Float.NaN; - private float mInsertionMarkerBottom = Float.NaN; - private int mInsertionMarkerFlags = 0; + + /** + * Adds the bounding box of the character specified with the index. + * + * @param index index of the character in Java chars units. Must be specified in + * ascending order across successive calls. + * @param left x coordinate of the left edge of the character in local coordinates. + * @param top y coordinate of the top edge of the character in local coordinates. + * @param right x coordinate of the right edge of the character in local coordinates. + * @param bottom y coordinate of the bottom edge of the character in local coordinates. + * @param flags flags for this character bounds. See {@link #FLAG_HAS_VISIBLE_REGION}, + * {@link #FLAG_HAS_INVISIBLE_REGION} and {@link #FLAG_IS_RTL}. These flags must be + * specified when necessary. + * @throws IllegalArgumentException If the index is a negative value, or not greater than + * all of the previously called indices. + */ + public Builder addCharacterBounds(final int index, final float left, final float top, + final float right, final float bottom, final int flags) { + if (index < 0) { + throw new IllegalArgumentException("index must not be a negative integer."); + } + if (mCharacterBoundsArrayBuilder == null) { + mCharacterBoundsArrayBuilder = new SparseRectFArrayBuilder(); + } + mCharacterBoundsArrayBuilder.append(index, left, top, right, bottom, flags); + return this; + } /** * Adds the bounding box of the character specified with the index. @@ -358,21 +406,25 @@ public final class CursorAnchorInfo implements Parcelable { * example. * @throws IllegalArgumentException If the index is a negative value, or not greater than * all of the previously called indices. + * @removed */ public Builder addCharacterRect(final int index, final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX, final float trailingEdgeY, final int flags) { - if (index < 0) { - throw new IllegalArgumentException("index must not be a negative integer."); - } - if (mCharacterRectBuilder == null) { - mCharacterRectBuilder = new SparseRectFArrayBuilder(); + final int newFlags; + final float left; + final float right; + if (leadingEdgeX <= trailingEdgeX) { + newFlags = flags; + left = leadingEdgeX; + right = trailingEdgeX; + } else { + newFlags = flags | FLAG_IS_RTL; + left = trailingEdgeX; + right = leadingEdgeX; } - mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX, - trailingEdgeY, flags); - return this; + return addCharacterBounds(index, left, leadingEdgeY, right, trailingEdgeY, newFlags); } - private SparseRectFArrayBuilder mCharacterRectBuilder = null; /** * Sets the matrix that transforms local coordinates into screen coordinates. @@ -384,8 +436,6 @@ public final class CursorAnchorInfo implements Parcelable { mMatrixInitialized = true; return this; } - private final Matrix mMatrix = new Matrix(Matrix.IDENTITY_MATRIX); - private boolean mMatrixInitialized = false; /** * @return {@link CursorAnchorInfo} using parameters in this {@link Builder}. @@ -394,13 +444,15 @@ public final class CursorAnchorInfo implements Parcelable { */ public CursorAnchorInfo build() { if (!mMatrixInitialized) { - // Coordinate transformation matrix is mandatory when positional parameters are - // specified. - if ((mCharacterRectBuilder != null && !mCharacterRectBuilder.isEmpty()) || - !Float.isNaN(mInsertionMarkerHorizontal) || - !Float.isNaN(mInsertionMarkerTop) || - !Float.isNaN(mInsertionMarkerBaseline) || - !Float.isNaN(mInsertionMarkerBottom)) { + // Coordinate transformation matrix is mandatory when at least one positional + // parameter is specified. + final boolean hasCharacterBounds = (mCharacterBoundsArrayBuilder != null + && !mCharacterBoundsArrayBuilder.isEmpty()); + if (hasCharacterBounds + || !Float.isNaN(mInsertionMarkerHorizontal) + || !Float.isNaN(mInsertionMarkerTop) + || !Float.isNaN(mInsertionMarkerBaseline) + || !Float.isNaN(mInsertionMarkerBottom)) { throw new IllegalArgumentException("Coordinate transformation matrix is " + "required when positional parameters are specified."); } @@ -424,8 +476,8 @@ public final class CursorAnchorInfo implements Parcelable { mInsertionMarkerBottom = Float.NaN; mMatrix.set(Matrix.IDENTITY_MATRIX); mMatrixInitialized = false; - if (mCharacterRectBuilder != null) { - mCharacterRectBuilder.reset(); + if (mCharacterBoundsArrayBuilder != null) { + mCharacterBoundsArrayBuilder.reset(); } } } @@ -440,8 +492,8 @@ public final class CursorAnchorInfo implements Parcelable { mInsertionMarkerTop = builder.mInsertionMarkerTop; mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline; mInsertionMarkerBottom = builder.mInsertionMarkerBottom; - mCharacterRects = builder.mCharacterRectBuilder != null ? - builder.mCharacterRectBuilder.build() : null; + mCharacterBoundsArray = builder.mCharacterBoundsArrayBuilder != null ? + builder.mCharacterBoundsArrayBuilder.build() : null; mMatrix = new Matrix(builder.mMatrix); } @@ -539,6 +591,19 @@ public final class CursorAnchorInfo implements Parcelable { /** * Returns a new instance of {@link RectF} that indicates the location of the character * specified with the index. + * @param index index of the character in a Java chars. + * @return the character bounds in local coordinates as a new instance of {@link RectF}. + */ + public RectF getCharacterBounds(final int index) { + if (mCharacterBoundsArray == null) { + return null; + } + return mCharacterBoundsArray.get(index); + } + + /** + * Returns a new instance of {@link RectF} that indicates the location of the character + * specified with the index. * <p> * Note that coordinates are not necessarily contiguous or even monotonous, especially when * RTL text and LTR text are mixed. @@ -549,28 +614,32 @@ public final class CursorAnchorInfo implements Parcelable { * the location. Note that the {@code left} field can be greater than the {@code right} field * if the character is in RTL text. Returns {@code null} if no location information is * available. + * @removed */ - // TODO: Prepare a document about the expected behavior for surrogate pairs, combining - // characters, and non-graphical chars. public RectF getCharacterRect(final int index) { - if (mCharacterRects == null) { - return null; + return getCharacterBounds(index); + } + + /** + * Returns the flags associated with the character bounds specified with the index. + * @param index index of the character in a Java chars. + * @return {@code 0} if no flag is specified. + */ + public int getCharacterBoundsFlags(final int index) { + if (mCharacterBoundsArray == null) { + return 0; } - return mCharacterRects.get(index); + return mCharacterBoundsArray.getFlags(index, 0); } /** * Returns the flags associated with the character rect specified with the index. * @param index index of the character in a Java chars. * @return {@code 0} if no flag is specified. + * @removed */ - // TODO: Prepare a document about the expected behavior for surrogate pairs, combining - // characters, and non-graphical chars. public int getCharacterRectFlags(final int index) { - if (mCharacterRects == null) { - return 0; - } - return mCharacterRects.getFlags(index, 0); + return getCharacterBoundsFlags(index); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index eef8554..94d52d5 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3325,8 +3325,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } if (dispatchNestedPreScroll(0, -rawDeltaY, mScrollConsumed, mScrollOffset)) { rawDeltaY += mScrollConsumed[1]; - scrollOffsetCorrection -= mScrollOffset[1]; - scrollConsumedCorrection -= mScrollConsumed[1]; + scrollOffsetCorrection = -mScrollOffset[1]; + scrollConsumedCorrection = mScrollConsumed[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } @@ -3437,7 +3437,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - mMotionY = y + scrollOffsetCorrection; + mMotionY = y + lastYCorrection + scrollOffsetCorrection; } mLastY = y + lastYCorrection + scrollOffsetCorrection; } @@ -3505,10 +3505,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mMotionCorrection = 0; View motionView = getChildAt(motionPosition - mFirstPosition); mMotionViewOriginalTop = motionView != null ? motionView.getTop() : 0; - mMotionY = y; + mMotionY = y + scrollOffsetCorrection; mMotionPosition = motionPosition; } - mLastY = y; + mLastY = y + lastYCorrection + scrollOffsetCorrection; mDirection = newDirection; } } @@ -3876,7 +3876,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mPositionScroller != null) { mPositionScroller.stop(); } - if (flingVelocity) { + if (flingVelocity && !dispatchNestedPreFling(0, -initialVelocity)) { dispatchNestedFling(0, -initialVelocity, false); } } @@ -4001,14 +4001,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * <p>Applications can use this method to manually initiate a fling as if the user * initiated it via touch interaction.</p> * - * @param velocityY Vertical velocity in pixels per second + * @param velocityY Vertical velocity in pixels per second. Note that this is velocity of + * content, not velocity of a touch that initiated the fling. */ public void fling(int velocityY) { if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); - mFlingRunnable.start(-velocityY); + mFlingRunnable.start(velocityY); } @Override diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 2729bd0..d77f0b2 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -125,6 +125,7 @@ public class DatePicker extends FrameLayout { final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, defStyleAttr, defStyleRes); final int mode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER); + final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0); a.recycle(); switch (mode) { @@ -136,6 +137,10 @@ public class DatePicker extends FrameLayout { mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes); break; } + + if (firstDayOfWeek != 0) { + setFirstDayOfWeek(firstDayOfWeek); + } } private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs, @@ -300,6 +305,47 @@ public class DatePicker extends FrameLayout { } /** + * Sets the first day of week. + * + * @param firstDayOfWeek The first day of the week conforming to the + * {@link CalendarView} APIs. + * @see Calendar#SUNDAY + * @see Calendar#MONDAY + * @see Calendar#TUESDAY + * @see Calendar#WEDNESDAY + * @see Calendar#THURSDAY + * @see Calendar#FRIDAY + * @see Calendar#SATURDAY + * + * @attr ref android.R.styleable#DatePicker_firstDayOfWeek + */ + public void setFirstDayOfWeek(int firstDayOfWeek) { + if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) { + throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7"); + } + mDelegate.setFirstDayOfWeek(firstDayOfWeek); + } + + /** + * Gets the first day of week. + * + * @return The first day of the week conforming to the {@link CalendarView} + * APIs. + * @see Calendar#SUNDAY + * @see Calendar#MONDAY + * @see Calendar#TUESDAY + * @see Calendar#WEDNESDAY + * @see Calendar#THURSDAY + * @see Calendar#FRIDAY + * @see Calendar#SATURDAY + * + * @attr ref android.R.styleable#DatePicker_firstDayOfWeek + */ + public int getFirstDayOfWeek() { + return mDelegate.getFirstDayOfWeek(); + } + + /** * Gets whether the {@link CalendarView} is shown. * * @return True if the calendar view is shown. @@ -315,7 +361,7 @@ public class DatePicker extends FrameLayout { * @return The calendar view. * @see #getCalendarViewShown() */ - public CalendarView getCalendarView () { + public CalendarView getCalendarView() { return mDelegate.getCalendarView(); } @@ -382,6 +428,9 @@ public class DatePicker extends FrameLayout { int getMonth(); int getDayOfMonth(); + void setFirstDayOfWeek(int firstDayOfWeek); + int getFirstDayOfWeek(); + void setMinDate(long minDate); Calendar getMinDate(); @@ -699,6 +748,16 @@ public class DatePicker extends FrameLayout { } @Override + public void setFirstDayOfWeek(int firstDayOfWeek) { + mCalendarView.setFirstDayOfWeek(firstDayOfWeek); + } + + @Override + public int getFirstDayOfWeek() { + return mCalendarView.getFirstDayOfWeek(); + } + + @Override public void setMinDate(long minDate) { mTempDate.setTimeInMillis(minDate); if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index eed49bf..b962962 100644 --- a/core/java/android/widget/DatePickerCalendarDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -49,6 +49,7 @@ import java.util.Locale; */ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements View.OnClickListener, DatePickerController { + private static final int USE_LOCALE = 0; private static final int UNINITIALIZED = -1; private static final int MONTH_AND_DAY_VIEW = 0; @@ -99,6 +100,8 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i private Calendar mMinDate; private Calendar mMaxDate; + private int mFirstDayOfWeek = USE_LOCALE; + private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>(); public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs, @@ -438,7 +441,15 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate i } @Override + public void setFirstDayOfWeek(int firstDayOfWeek) { + mFirstDayOfWeek = firstDayOfWeek; + } + + @Override public int getFirstDayOfWeek() { + if (mFirstDayOfWeek != USE_LOCALE) { + return mFirstDayOfWeek; + } return mCurrentDate.getFirstDayOfWeek(); } diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java index 6a074da..059709d 100644 --- a/core/java/android/widget/DatePickerController.java +++ b/core/java/android/widget/DatePickerController.java @@ -35,6 +35,7 @@ interface DatePickerController { Calendar getSelectedDay(); + void setFirstDayOfWeek(int firstDayOfWeek); int getFirstDayOfWeek(); int getMinYear(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 22138d0..3f168e8 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3060,47 +3060,69 @@ public class Editor { final CharSequence composingText = text.subSequence(composingTextStart, composingTextEnd); builder.setComposingText(composingTextStart, composingText); - } - // TODO: Optimize this loop by caching the result. - for (int offset = composingTextStart; offset < composingTextEnd; offset++) { - if (offset < 0) { - continue; - } - final boolean isRtl = layout.isRtlCharAt(offset); - final int line = layout.getLineForOffset(offset); - final int nextCharIndex = offset + 1; - final float localLeadingEdgeX = layout.getPrimaryHorizontal(offset); - final float localTrailingEdgeX; - if (nextCharIndex != layout.getLineEnd(line)) { - localTrailingEdgeX = layout.getPrimaryHorizontal(nextCharIndex); - } else if (isRtl) { - localTrailingEdgeX = layout.getLineLeft(line); - } else { - localTrailingEdgeX = layout.getLineRight(line); - } - final float leadingEdgeX = localLeadingEdgeX - + viewportToContentHorizontalOffset; - final float trailingEdgeX = localTrailingEdgeX - + viewportToContentHorizontalOffset; - final float top = layout.getLineTop(line) + viewportToContentVerticalOffset; - final float bottom = layout.getLineBottom(line) - + viewportToContentVerticalOffset; - // TODO: Check right-top and left-bottom as well. - final boolean isLeadingEdgeTopVisible = isPositionVisible(leadingEdgeX, top); - final boolean isTrailingEdgeBottomVisible = - isPositionVisible(trailingEdgeX, bottom); - int characterRectFlags = 0; - if (isLeadingEdgeTopVisible || isTrailingEdgeBottomVisible) { - characterRectFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; - } - if (!isLeadingEdgeTopVisible || !isTrailingEdgeBottomVisible) { - characterRectFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + + final int minLine = layout.getLineForOffset(composingTextStart); + final int maxLine = layout.getLineForOffset(composingTextEnd - 1); + for (int line = minLine; line <= maxLine; ++line) { + final int lineStart = layout.getLineStart(line); + final int lineEnd = layout.getLineEnd(line); + final int offsetStart = Math.max(lineStart, composingTextStart); + final int offsetEnd = Math.min(lineEnd, composingTextEnd); + final boolean ltrLine = + layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT; + final float[] widths = new float[offsetEnd - offsetStart]; + layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths); + final float top = layout.getLineTop(line); + final float bottom = layout.getLineBottom(line); + for (int offset = offsetStart; offset < offsetEnd; ++offset) { + final float charWidth = widths[offset - offsetStart]; + final boolean isRtl = layout.isRtlCharAt(offset); + final float primary = layout.getPrimaryHorizontal(offset); + final float secondary = layout.getSecondaryHorizontal(offset); + // TODO: This doesn't work perfectly for text with custom styles and + // TAB chars. + final float left; + final float right; + if (ltrLine) { + if (isRtl) { + left = secondary - charWidth; + right = secondary; + } else { + left = primary; + right = primary + charWidth; + } + } else { + if (!isRtl) { + left = secondary; + right = secondary + charWidth; + } else { + left = primary - charWidth; + right = primary; + } + } + // TODO: Check top-right and bottom-left as well. + final float localLeft = left + viewportToContentHorizontalOffset; + final float localRight = right + viewportToContentHorizontalOffset; + final float localTop = top + viewportToContentVerticalOffset; + final float localBottom = bottom + viewportToContentVerticalOffset; + final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop); + final boolean isBottomRightVisible = + isPositionVisible(localRight, localBottom); + int characterBoundsFlags = 0; + if (isTopLeftVisible || isBottomRightVisible) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isTopLeftVisible || !isTopLeftVisible) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + } + if (isRtl) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL; + } + // Here offset is the index in Java chars. + builder.addCharacterBounds(offset, localLeft, localTop, localRight, + localBottom, characterBoundsFlags); + } } - // Here offset is the index in Java chars. - // TODO: We must have a well-defined specification. For example, how - // surrogate pairs and composition letters are handled must be documented. - builder.addCharacterRect(offset, leadingEdgeX, top, trailingEdgeX, bottom, - characterRectFlags); } } @@ -3127,6 +3149,9 @@ public class Editor { if (!isTopVisible || !isBottomVisible) { insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; } + if (layout.isRtlCharAt(offset)) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL; + } builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags); } |
