diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-02-25 03:10:24 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-25 03:10:24 +0000 |
| commit | 0cbb2687ed5bf2be263a56950e89eb8020fac75f (patch) | |
| tree | d9c5b499633e85e108cd73dcdcfd49057dc462f9 | |
| parent | 599d34971224d0f9f6ebb813b786eac1501c680b (diff) | |
| parent | 85cf41fcc3b2a7882355ad9921cc9ca168e4a2b3 (diff) | |
Merge "Move PiP animation to SysUI package" into rvc-dev
51 files changed, 1085 insertions, 2766 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 2d15c0ea0ffb..d32c77f4cd25 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -118,7 +118,6 @@ package android.app { method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException; method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void requestPictureInPictureMode(@NonNull android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException; diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index 9ba56cf40161..d48b35b6f0c8 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -362,25 +362,6 @@ public class ActivityTaskManager { } /** - * Resize the input stack id to the given bounds with animate setting. - * @param stackId Id of the stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animate Whether we should play an animation for resizing stack. - */ - @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void resizePinnedStack(int stackId, Rect bounds, boolean animate) { - try { - if (animate) { - getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */); - } else { - getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Resize task to given bounds. * @param taskId Id of task to resize. * @param bounds Bounds to resize task. diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 5b61402314c4..4e3aa7d8ebc0 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -236,33 +236,9 @@ interface IActivityTaskManager { */ boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop); void moveTaskToStack(int taskId, int stackId, boolean toTop); - /** - * Resizes the input pinned stack to the given bounds with animation. - * - * @param stackId Id of the pinned stack to resize. - * @param bounds Bounds to resize the stack to or {@code null} for fullscreen. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration); boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop, boolean animate, in Rect initialBounds, boolean showRecents); /** - * Use the offset to adjust the stack boundary with animation. - * - * @param stackId Id of the stack to adjust. - * @param compareBounds Offset is only applied if the current pinned stack bounds is equal to - * the compareBounds. - * @param xOffset The horizontal offset. - * @param yOffset The vertical offset. - * @param animationDuration The duration of the resize animation in milliseconds or -1 if the - * default animation duration should be used. - * @throws RemoteException - */ - void offsetPinnedStackBounds(int stackId, in Rect compareBounds, int xOffset, int yOffset, - int animationDuration); - /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ @@ -371,23 +347,10 @@ interface IActivityTaskManager { void startLocalVoiceInteraction(in IBinder token, in Bundle options); void stopLocalVoiceInteraction(in IBinder token); boolean supportsLocalVoiceInteraction(); - void notifyPinnedStackAnimationStarted(); - void notifyPinnedStackAnimationEnded(); // Get device configuration ConfigurationInfo getDeviceConfigurationInfo(); - /** - * Resizes the pinned stack. - * - * @param pinnedBounds The bounds for the pinned stack. - * @param tempPinnedTaskBounds The temporary bounds for the tasks in the pinned stack, which - * might be different from the stack bounds to allow more - * flexibility while resizing, or {@code null} if they should be the - * same as the stack bounds. - */ - void resizePinnedStack(in Rect pinnedBounds, in Rect tempPinnedTaskBounds); - void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback, in CharSequence message); diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 37bdda0b0393..28b28dad5b82 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -46,16 +46,6 @@ oneway interface ITaskStackListener { void onPinnedActivityRestartAttempt(boolean clearedTask); /** - * Called whenever the pinned stack is starting animating a resize. - */ - void onPinnedStackAnimationStarted(); - - /** - * Called whenever the pinned stack is done animating a resize. - */ - void onPinnedStackAnimationEnded(); - - /** * Called when we launched an activity that we forced to be resizable. * * @param packageName Package name of the top activity in the task. diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index da0aadb3ea48..b892b8e51c88 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -58,16 +58,6 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { @Override @UnsupportedAppUsage - public void onPinnedStackAnimationStarted() throws RemoteException { - } - - @Override - @UnsupportedAppUsage - public void onPinnedStackAnimationEnded() throws RemoteException { - } - - @Override - @UnsupportedAppUsage public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { } diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index cb82f168d991..1cf83a3f1e79 100644 --- a/core/java/android/view/IPinnedStackController.aidl +++ b/core/java/android/view/IPinnedStackController.aidl @@ -25,32 +25,8 @@ import android.graphics.Rect; * @hide */ interface IPinnedStackController { - - /** - * Notifies the controller that the PiP is currently minimized. - */ - oneway void setIsMinimized(boolean isMinimized); - /** * @return what WM considers to be the current device rotation. */ int getDisplayRotation(); - - /** - * Notifies the controller to actually start the PiP animation. - * The bounds would be calculated based on the last save reentry fraction internally. - * {@param destinationBounds} is the stack bounds of the final PiP window - * and {@param sourceRectHint} is the source bounds hint used when entering picture-in-picture, - * expect the same bound passed via IPinnedStackListener#onPrepareAnimation. - * {@param animationDuration} suggests the animation duration transitioning to PiP window. - */ - void startAnimation(in Rect destinationBounds, in Rect sourceRectHint, int animationDuration); - - /** - * Notifies the controller to reset on bounds animation, if there is any. - * This could happen when screen rotation is happening and we need to notify the WM to reset - * any running bounds animation on the pinned stack. - * {@param bounds} here is the final destination bounds. - */ - void resetBoundsAnimation(in Rect bounds); } diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl index d01c9330dcb2..596d55aebc6d 100644 --- a/core/java/android/view/IPinnedStackListener.aidl +++ b/core/java/android/view/IPinnedStackListener.aidl @@ -43,8 +43,7 @@ oneway interface IPinnedStackListener { * pinned stack (the final bounds if animating, the current bounds if not), * which may be helpful in calculating dependent animation bounds. */ - void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment); + void onMovementBoundsChanged(in Rect animatingBounds, boolean fromImeAdjustment); /** * Called when window manager decides to adjust the pinned stack bounds because of the IME, or @@ -55,12 +54,6 @@ oneway interface IPinnedStackListener { void onImeVisibilityChanged(boolean imeVisible, int imeHeight); /** - * Called when window manager decides to adjust the minimized state, or when the listener - * is first registered to allow the listener to synchronized its state with the controller. - */ - void onMinimizedStateChanged(boolean isMinimized); - - /** * Called when the set of actions for the current PiP activity changes, or when the listener * is first registered to allow the listener to synchronized its state with the controller. */ @@ -99,13 +92,4 @@ oneway interface IPinnedStackListener { * Called by the window manager when the aspect ratio is reset. */ void onAspectRatioChanged(float aspectRatio); - - /** - * Called by the window manager to notify the listener to prepare for PiP animation. - * Internally, the target bounds would be calculated from the given {@param aspectRatio} - * and {@param bounds}, the saved reentry snap fraction also contributes. - * Caller would wait for a IPinnedStackController#startAnimation callback to actually - * start the animation, see details in IPinnedStackController. - */ - void onPrepareAnimation(in Rect sourceRectHint, float aspectRatio, in Rect bounds); } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 762366eb6295..530dffbf0620 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -77,13 +77,6 @@ interface IRecentsAnimationController { void hideCurrentInputMethod(); /** - * This call is deprecated, use #setDeferCancelUntilNextTransition() instead - * TODO(138144750): Remove this method once there are no callers - * @deprecated - */ - void setCancelWithDeferredScreenshot(boolean screenshot); - - /** * Clean up the screenshot of previous task which was created during recents animation that * was cancelled by a stack order change. * diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl new file mode 100644 index 000000000000..97aa512ea7df --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.recents; + +/** + * Listener interface that Launcher attaches to SystemUI to get + * pinned stack animation callbacks. + */ +oneway interface IPinnedStackAnimationListener { + /** + * Notifies the pinned stack animation is started. + */ + void onPinnedStackAnimationStarted(); +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index b1d39f59f789..80fd826f28c6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -22,9 +22,11 @@ import android.graphics.Rect; import android.os.Bundle; import android.view.MotionEvent; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + /** * Temporary callbacks into SystemUI. - * Next id = 22 + * Next id = 25 */ interface ISystemUiProxy { @@ -121,11 +123,21 @@ interface ISystemUiProxy { /** * Handle the provided image as if it was a screenshot. */ - void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, + void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen, in Insets visibleInsets, int taskId) = 21; /** * Sets the split-screen divider minimized state */ void setSplitScreenMinimized(boolean minimized) = 22; + + /* + * Notifies that the swipe-to-home (recents animation) is finished. + */ + void notifySwipeToHomeFinished() = 23; + + /** + * Sets listener to get pinned stack animation callbacks. + */ + void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java index 8c0ffb82f0ed..360244c9af52 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java @@ -53,11 +53,9 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { for (PinnedStackListener listener : mListeners) { - listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment, - fromShelfAdjustment); + listener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } } @@ -69,13 +67,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - for (PinnedStackListener listener : mListeners) { - listener.onMinimizedStateChanged(isMinimized); - } - } - - @Override public void onActionsChanged(ParceledListSlice actions) { for (PinnedStackListener listener : mListeners) { listener.onActionsChanged(actions); @@ -117,13 +108,6 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { } } - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - for (PinnedStackListener listener : mListeners) { - listener.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - } - } - /** * A counterpart of {@link IPinnedStackListener} with empty implementations. * Subclasses can ignore those methods they do not intend to take action upon. @@ -131,13 +115,10 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public static class PinnedStackListener { public void onListenerRegistered(IPinnedStackController controller) {} - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) {} + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) {} public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {} - public void onMinimizedStateChanged(boolean isMinimized) {} - public void onActionsChanged(ParceledListSlice actions) {} public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {} @@ -149,7 +130,5 @@ public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { public void onConfigurationChanged() {} public void onAspectRatioChanged(float aspectRatio) {} - - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) {} } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 748f356c25b9..6cd6118ede6b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -91,15 +91,6 @@ public class RecentsAnimationControllerCompat { } } - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - try { - mAnimationController.setCancelWithDeferredScreenshot(screenshot); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set cancel with deferred screenshot", e); - } - } - public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { try { mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index 5f92b2811807..1c6223b847de 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -20,8 +20,6 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.ITaskStackListener; import android.content.ComponentName; import android.os.IBinder; -import android.os.UserHandle; -import android.util.Log; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -40,8 +38,6 @@ public abstract class TaskStackChangeListener { public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } - public void onPinnedStackAnimationStarted() { } - public void onPinnedStackAnimationEnded() { } public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } @@ -117,22 +113,4 @@ public abstract class TaskStackChangeListener { /** @see ITaskStackListener#onRecentTaskListFrozenChanged(boolean) */ public void onRecentTaskListFrozenChanged(boolean frozen) { } - - /** - * Checks that the current user matches the process. Since - * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of - * {@link TaskStackChangeListener} should make this call to verify that we don't act on events - * originating from another user's interactions. - */ - protected final boolean checkCurrentUserId(int currentUserId, boolean debug) { - int processUserId = UserHandle.myUserId(); - if (processUserId != currentUserId) { - if (debug) { - Log.d("TaskStackChangeListener", "UID mismatch. Process is uid=" + processUserId - + " and the current user is uid=" + currentUserId); - } - return false; - } - return true; - } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index acce41cc942e..cbdd3f8191f4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -128,18 +128,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onPinnedStackAnimationStarted() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED); - } - - @Override - public void onPinnedStackAnimationEnded() throws RemoteException { - mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED); - mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED); - } - - @Override public void onActivityForcedResizable(String packageName, int taskId, int reason) throws RemoteException { mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName) @@ -249,11 +237,9 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_SNAPSHOT_CHANGED = 2; private static final int ON_ACTIVITY_PINNED = 3; private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4; - private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5; private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6; private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7; private static final int ON_TASK_PROFILE_LOCKED = 8; - private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9; private static final int ON_ACTIVITY_UNPINNED = 10; private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11; private static final int ON_TASK_CREATED = 12; @@ -317,18 +303,6 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } - case ON_PINNED_STACK_ANIMATION_STARTED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationStarted(); - } - break; - } - case ON_PINNED_STACK_ANIMATION_ENDED: { - for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onPinnedStackAnimationEnded(); - } - break; - } case ON_ACTIVITY_FORCED_RESIZABLE: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { mTaskStackListeners.get(i).onActivityForcedResizable( diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java index adee7f23e709..38744fe1b670 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java @@ -18,6 +18,8 @@ package com.android.systemui.pip; import android.content.res.Configuration; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; + import java.io.PrintWriter; @@ -27,5 +29,7 @@ public interface BasePipManager { default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} void onConfigurationChanged(Configuration newConfig); default void setShelfHeight(boolean visible, int height) {} + default void setPinnedStackAnimationType(int animationType) {} + default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {} default void dump(PrintWriter pw) {} } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java new file mode 100644 index 000000000000..b5fd406de368 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.annotation.IntDef; +import android.annotation.MainThread; +import android.content.Context; +import android.graphics.Rect; +import android.os.RemoteException; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controller class of PiP animations (both from and to PiP mode). + */ +public class PipAnimationController { + private static final float FRACTION_START = 0f; + private static final float FRACTION_END = 1f; + + public static final int DURATION_NONE = 0; + public static final int DURATION_DEFAULT_MS = 425; + public static final int ANIM_TYPE_BOUNDS = 0; + public static final int ANIM_TYPE_ALPHA = 1; + + @IntDef(prefix = { "ANIM_TYPE_" }, value = { + ANIM_TYPE_BOUNDS, + ANIM_TYPE_ALPHA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AnimationType {} + + private final Interpolator mFastOutSlowInInterpolator; + + private PipTransitionAnimator mCurrentAnimator; + + PipAnimationController(Context context) { + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float alphaStart, float alphaEnd) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.updateEndValue(alphaEnd); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip, + destinationBounds, alphaStart, alphaEnd)); + } + return mCurrentAnimator; + } + + @MainThread + PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip, + Rect startBounds, Rect endBounds) { + if (mCurrentAnimator == null) { + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS + && mCurrentAnimator.isRunning()) { + mCurrentAnimator.setDestinationBounds(endBounds); + // construct new Rect instances in case they are recycled + mCurrentAnimator.updateEndValue(new Rect(endBounds)); + } else { + mCurrentAnimator.cancel(); + mCurrentAnimator = setupPipTransitionAnimator( + PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds)); + } + return mCurrentAnimator; + } + + private PipTransitionAnimator setupPipTransitionAnimator(PipTransitionAnimator animator) { + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setFloatValues(FRACTION_START, FRACTION_END); + return animator; + } + + /** + * Additional callback interface for PiP animation + */ + public static class PipAnimationCallback { + /** + * Called when PiP animation is started. + */ + public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is ended. + */ + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipTransitionAnimator animator) {} + + /** + * Called when PiP animation is cancelled. + */ + public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {} + } + + /** + * Animator for PiP transition animation which supports both alpha and bounds animation. + * @param <T> Type of property to animate, either alpha (float) or bounds (Rect) + */ + public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements + ValueAnimator.AnimatorUpdateListener, + ValueAnimator.AnimatorListener { + private final IWindowContainer mWindowContainer; + private final boolean mScheduleFinishPip; + private final SurfaceControl mLeash; + private final @AnimationType int mAnimationType; + private final Rect mDestinationBounds = new Rect(); + + private T mStartValue; + private T mEndValue; + private T mCurrentValue; + private PipAnimationCallback mPipAnimationCallback; + private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; + + private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip, + @AnimationType int animationType, Rect destinationBounds, + T startValue, T endValue) { + mWindowContainer = wc; + mScheduleFinishPip = scheduleFinishPip; + try { + mLeash = wc.getLeash(); + mAnimationType = animationType; + mDestinationBounds.set(destinationBounds); + mStartValue = startValue; + mEndValue = endValue; + addListener(this); + addUpdateListener(this); + mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onAnimationStart(Animator animation) { + mCurrentValue = mStartValue; + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this); + } + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), + animation.getAnimatedFraction()); + } + + @Override + public void onAnimationEnd(Animator animation) { + mCurrentValue = mEndValue; + final SurfaceControl.Transaction tx = newSurfaceControlTransaction(); + applySurfaceControlTransaction(mLeash, tx, FRACTION_END); + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + if (mPipAnimationCallback != null) { + mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this); + } + } + + @Override public void onAnimationRepeat(Animator animation) {} + + @AnimationType int getAnimationType() { + return mAnimationType; + } + + PipTransitionAnimator<T> setPipAnimationCallback(PipAnimationCallback callback) { + mPipAnimationCallback = callback; + return this; + } + + boolean shouldScheduleFinishPip() { + return mScheduleFinishPip; + } + + T getStartValue() { + return mStartValue; + } + + T getEndValue() { + return mEndValue; + } + + Rect getDestinationBounds() { + return mDestinationBounds; + } + + void setDestinationBounds(Rect destinationBounds) { + mDestinationBounds.set(destinationBounds); + } + + void setCurrentValue(T value) { + mCurrentValue = value; + } + + /** + * Updates the {@link #mEndValue}. + * + * NOTE: Do not forget to call {@link #setDestinationBounds(Rect)} for bounds animation. + * This is typically used when we receive a shelf height adjustment during the bounds + * animation. In which case we can update the end bounds and keep the existing animation + * running instead of cancelling it. + */ + void updateEndValue(T endValue) { + mEndValue = endValue; + mStartValue = mCurrentValue; + } + + SurfaceControl.Transaction newSurfaceControlTransaction() { + return mSurfaceControlTransactionFactory.getTransaction(); + } + + @VisibleForTesting + void setSurfaceControlTransactionFactory(SurfaceControlTransactionFactory factory) { + mSurfaceControlTransactionFactory = factory; + } + + abstract void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction); + + static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float startValue, float endValue) { + return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA, + destinationBounds, startValue, endValue) { + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction; + setCurrentValue(alpha); + tx.setAlpha(leash, alpha); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + final Rect bounds = getDestinationBounds(); + tx.setPosition(leash, bounds.left, bounds.top) + .setWindowCrop(leash, bounds.width(), bounds.height()); + } + tx.apply(); + } + }; + } + + static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip, + Rect startValue, Rect endValue) { + // construct new Rect instances in case they are recycled + return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS, + endValue, new Rect(startValue), new Rect(endValue)) { + private final Rect mTmpRect = new Rect(); + + private int getCastedFractionValue(float start, float end, float fraction) { + return (int) (start * (1 - fraction) + end * fraction + .5f); + } + + @Override + void applySurfaceControlTransaction(SurfaceControl leash, + SurfaceControl.Transaction tx, float fraction) { + final Rect start = getStartValue(); + final Rect end = getEndValue(); + mTmpRect.set( + getCastedFractionValue(start.left, end.left, fraction), + getCastedFractionValue(start.top, end.top, fraction), + getCastedFractionValue(start.right, end.right, fraction), + getCastedFractionValue(start.bottom, end.bottom, fraction)); + setCurrentValue(mTmpRect); + tx.setPosition(leash, mTmpRect.left, mTmpRect.top) + .setWindowCrop(leash, mTmpRect.width(), mTmpRect.height()); + if (Float.compare(fraction, FRACTION_START) == 0) { + // Ensure the start condition + tx.setAlpha(leash, 1f); + } + tx.apply(); + } + }; + } + } + + interface SurfaceControlTransactionFactory { + SurfaceControl.Transaction getTransaction(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index 41b31306a931..8c3ccaab8249 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -36,7 +36,6 @@ import android.util.Size; import android.util.TypedValue; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; import android.view.WindowManagerGlobal; @@ -56,9 +55,7 @@ public class PipBoundsHandler { private final IWindowManager mWindowManager; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final Rect mStableInsets = new Rect(); private final Rect mTmpInsets = new Rect(); - private final Point mTmpDisplaySize = new Point(); /** * Tracks the destination bounds, used for any following @@ -66,7 +63,6 @@ public class PipBoundsHandler { */ private final Rect mLastDestinationBounds = new Rect(); - private IPinnedStackController mPinnedStackController; private ComponentName mLastPipComponentName; private float mReentrySnapFraction = INVALID_SNAP_FRACTION; private Size mReentrySize = null; @@ -80,7 +76,6 @@ public class PipBoundsHandler { private Point mScreenEdgeInsets; private int mCurrentMinSize; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private boolean mIsShelfShowing; @@ -123,10 +118,6 @@ public class PipBoundsHandler { com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); } - public void setPinnedStackController(IPinnedStackController controller) { - mPinnedStackController = controller; - } - public void setMinEdgeSize(int minEdgeSize) { mCurrentMinSize = minEdgeSize; } @@ -155,14 +146,6 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on minimized state change. - */ - public void onMinimizedStateChanged(boolean minimized) { - mIsMinimized = minimized; - mSnapAlgorithm.setMinimized(minimized); - } - - /** * Responds to IPinnedStackListener on movement bounds change. * Note that both inset and normal bounds will be calculated here rather than in the caller. */ @@ -238,9 +221,9 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on preparing the pinned stack animation. + * @return {@link Rect} of the destination PiP window bounds. */ - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { + Rect getDestinationBounds(float aspectRatio, Rect bounds) { final Rect destinationBounds; final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); if (bounds == null) { @@ -253,17 +236,16 @@ public class PipBoundsHandler { false /* useCurrentMinEdgeSize */); } if (destinationBounds.equals(bounds)) { - return; + return bounds; } mAspectRatio = aspectRatio; onResetReentryBoundsUnchecked(); - try { - mPinnedStackController.startAnimation(destinationBounds, sourceRectHint, - -1 /* animationDuration */); - mLastDestinationBounds.set(destinationBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to start PiP animation from SysUI", e); - } + mLastDestinationBounds.set(destinationBounds); + return destinationBounds; + } + + float getDefaultAspectRatio() { + return mDefaultAspectRatio; } /** @@ -307,18 +289,10 @@ public class PipBoundsHandler { false /* adjustForIme */); mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); - } - try { - outBounds.set(postChangeStackBounds); - mLastDestinationBounds.set(outBounds); - mPinnedStackController.resetBoundsAnimation(outBounds); - t.setBounds(pinnedStackInfo.stackToken, outBounds); - } catch (RemoteException e) { - Log.e(TAG, "Failed to resize PiP on display rotation", e); - } + outBounds.set(postChangeStackBounds); + mLastDestinationBounds.set(outBounds); + t.setBounds(pinnedStackInfo.stackToken, outBounds); return true; } @@ -370,9 +344,6 @@ public class PipBoundsHandler { final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f); stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight()); mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); - if (mIsMinimized) { - applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds)); - } } /** @@ -436,20 +407,6 @@ public class PipBoundsHandler { } /** - * Applies the minimized offsets to the given stack bounds. - */ - private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) { - mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); - try { - mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets); - mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, - mStableInsets); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get stable insets from WM", e); - } - } - - /** * @return the default snap fraction to apply instead of the default gravity when calculating * the default stack bounds when first entering PiP. */ @@ -486,7 +443,6 @@ public class PipBoundsHandler { pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio); pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java index f3e707c6408c..6b89718acee8 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java @@ -63,14 +63,9 @@ public class PipSnapAlgorithm { private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - private final int mMinimizedVisibleSize; - private boolean mIsMinimized; - public PipSnapAlgorithm(Context context) { Resources res = context.getResources(); mContext = context; - mMinimizedVisibleSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.pip_minimized_visible_size); mDefaultSizePercent = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( @@ -92,13 +87,6 @@ public class PipSnapAlgorithm { } /** - * Sets the PIP's minimized state. - */ - public void setMinimized(boolean isMinimized) { - mIsMinimized = isMinimized; - } - - /** * @return the closest absolute snap stack bounds for the given {@param stackBounds} moving at * the given {@param velocityX} and {@param velocityY}. The {@param movementBounds} should be * those for the given {@param stackBounds}. @@ -235,20 +223,6 @@ public class PipSnapAlgorithm { } /** - * Applies the offset to the {@param stackBounds} to adjust it to a minimized state. - */ - public void applyMinimizedOffset(Rect stackBounds, Rect movementBounds, Point displaySize, - Rect stableInsets) { - if (stackBounds.left <= movementBounds.centerX()) { - stackBounds.offsetTo(stableInsets.left + mMinimizedVisibleSize - stackBounds.width(), - stackBounds.top); - } else { - stackBounds.offsetTo(displaySize.x - stableInsets.right - mMinimizedVisibleSize, - stackBounds.top); - } - } - - /** * @return returns a fraction that describes where along the {@param movementBounds} the * {@param stackBounds} are. If the {@param stackBounds} are not currently on the * {@param movementBounds} exactly, then they will be snapped to the movement bounds. @@ -402,16 +376,11 @@ public class PipSnapAlgorithm { * the new bounds out to {@param boundsOut}. */ private void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) { - // If the stackBounds are minimized, then it should only be snapped back horizontally final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right, stackBounds.left)); final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom, stackBounds.top)); boundsOut.set(stackBounds); - if (mIsMinimized) { - boundsOut.offsetTo(boundedLeft, boundedTop); - return; - } // Otherwise, just find the closest edge final int fromLeft = Math.abs(stackBounds.left - movementBounds.left); @@ -479,7 +448,5 @@ public class PipSnapAlgorithm { pw.println(prefix + PipSnapAlgorithm.class.getSimpleName()); pw.println(innerPrefix + "mSnapMode=" + mSnapMode); pw.println(innerPrefix + "mOrientation=" + mOrientation); - pw.println(innerPrefix + "mMinimizedVisibleSize=" + mMinimizedVisibleSize); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java new file mode 100644 index 000000000000..1555153e8d4f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; +import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS; +import static com.android.systemui.pip.PipAnimationController.DURATION_NONE; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.ITaskOrganizerController; +import android.app.PictureInPictureParams; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.DisplayInfo; +import android.view.ITaskOrganizer; +import android.view.IWindowContainer; +import android.view.SurfaceControl; +import android.view.WindowContainerTransaction; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Manages PiP tasks such as resize and offset. + * + * This class listens on {@link ITaskOrganizer} callbacks for windowing mode change + * both to and from PiP and issues corresponding animation if applicable. + * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running + * and files a final {@link WindowContainerTransaction} at the end of the transition. + * + * This class is also responsible for general resize/offset PiP operations within SysUI component, + * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. + */ +public class PipTaskOrganizer extends ITaskOrganizer.Stub { + private static final String TAG = PipTaskOrganizer.class.getSimpleName(); + + private final Handler mMainHandler; + private final ITaskOrganizerController mTaskOrganizerController; + private final PipBoundsHandler mPipBoundsHandler; + private final PipAnimationController mPipAnimationController; + private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); + private final Rect mDisplayBounds = new Rect(); + private final Rect mLastReportedBounds = new Rect(); + + private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = + new PipAnimationController.PipAnimationCallback() { + @Override + public void onPipAnimationStart(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionStarted(); + } + }); + } + + @Override + public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionFinished(); + } + }); + final Rect destinationBounds = animator.getDestinationBounds(); + mLastReportedBounds.set(destinationBounds); + try { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (animator.shouldScheduleFinishPip()) { + wct.scheduleFinishEnterPip(wc, destinationBounds); + } else { + wct.setBounds(wc, destinationBounds); + } + wct.setBoundsChangeTransaction(wc, tx); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.e(TAG, "Failed to apply container transaction", e); + } + } + + @Override + public void onPipAnimationCancel(IWindowContainer wc, + PipAnimationController.PipTransitionAnimator animator) { + mMainHandler.post(() -> { + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionCanceled(); + } + }); + } + }; + + private ActivityManager.RunningTaskInfo mTaskInfo; + private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS; + + public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) { + mMainHandler = new Handler(Looper.getMainLooper()); + mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController(); + mPipBoundsHandler = boundsHandler; + mPipAnimationController = new PipAnimationController(context); + } + + /** + * Resize the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void resizePinnedStack(Rect destinationBounds, int durationMs) { + Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer"); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip */, + mLastReportedBounds, destinationBounds, durationMs); + } + + /** + * Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE} + */ + public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) { + if (mTaskInfo == null) { + Log.w(TAG, "mTaskInfo is not set"); + return; + } + final Rect destinationBounds = new Rect(originalBounds); + destinationBounds.offset(xOffset, yOffset); + resizePinnedStackInternal(mTaskInfo.token, false /* scheduleFinishPip*/, + originalBounds, destinationBounds, durationMs); + } + + /** + * Registers {@link PipTransitionCallback} to receive transition callbacks. + */ + public void registerPipTransitionCallback(PipTransitionCallback callback) { + mPipTransitionCallbacks.add(callback); + } + + /** + * Sets the preferred animation type for one time. + * This is typically used to set the animation type to {@link #ANIM_TYPE_ALPHA}. + */ + public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) { + mOneShotAnimationType = animationType; + } + + /** + * Updates the display dimension with given {@link DisplayInfo} + */ + public void onDisplayInfoChanged(DisplayInfo displayInfo) { + mDisplayBounds.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } + + /** + * Callback to issue the final {@link WindowContainerTransaction} on end of movements. + * @param destinationBounds the final bounds. + */ + public void onMotionMovementEnd(Rect destinationBounds) { + try { + mLastReportedBounds.set(destinationBounds); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mTaskInfo.token, destinationBounds); + mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */); + } catch (RemoteException e) { + Log.w(TAG, "Failed to apply window container transaction", e); + } + } + + @Override + public void taskAppeared(ActivityManager.RunningTaskInfo info) { + Objects.requireNonNull(info, "Requires RunningTaskInfo"); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + mTaskInfo = info; + if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { + final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); + resizePinnedStackInternal(mTaskInfo.token, true /* scheduleFinishPip */, + currentBounds, destinationBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(mTaskInfo.token, true /* scheduleFinishPip */, + destinationBounds, 0f, 1f) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(PipAnimationController.DURATION_DEFAULT_MS) + .start()); + mOneShotAnimationType = ANIM_TYPE_BOUNDS; + } else { + throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); + } + } + + @Override + public void taskVanished(IWindowContainer token) { + Objects.requireNonNull(token, "Requires valid IWindowContainer"); + if (token.asBinder() != mTaskInfo.token.asBinder()) { + Log.wtf(TAG, "Unrecognized token: " + token); + return; + } + resizePinnedStackInternal(token, false /* scheduleFinishPip */, + mLastReportedBounds, mDisplayBounds, + PipAnimationController.DURATION_DEFAULT_MS); + } + + @Override + public void transactionReady(int id, SurfaceControl.Transaction t) { + } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( + getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */); + Objects.requireNonNull(destinationBounds, "Missing destination bounds"); + resizePinnedStack(destinationBounds, PipAnimationController.DURATION_DEFAULT_MS); + } + + private void resizePinnedStackInternal(IWindowContainer wc, boolean scheduleFinishPip, + Rect currentBounds, Rect destinationBounds, int animationDurationMs) { + try { + // Could happen when dismissPip + if (wc == null || wc.getLeash() == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + final SurfaceControl leash = wc.getLeash(); + if (animationDurationMs == DURATION_NONE) { + // Directly resize if no animation duration is set. When fling, wait for final + // callback to issue the proper WindowContainerTransaction with destination bounds. + new SurfaceControl.Transaction() + .setPosition(leash, destinationBounds.left, destinationBounds.top) + .setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) + .apply(); + } else { + mMainHandler.post(() -> mPipAnimationController + .getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds) + .setPipAnimationCallback(mPipAnimationCallback) + .setDuration(animationDurationMs) + .start()); + } + } catch (RemoteException e) { + Log.w(TAG, "Abort animation, invalid window container", e); + } catch (Exception e) { + Log.e(TAG, "Should not reach here, terrible thing happened", e); + } + } + + private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) { + return params == null + ? mPipBoundsHandler.getDefaultAspectRatio() + : params.getAspectRatio(); + } + + /** + * Callback interface for PiP transitions (both from and to PiP mode) + */ + public interface PipTransitionCallback { + /** + * Callback when the pip transition is started. + */ + void onPipTransitionStarted(); + + /** + * Callback when the pip transition is finished. + */ + void onPipTransitionFinished(); + + /** + * Callback when the pip transition is cancelled. + */ + void onPipTransitionCanceled(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java index 599c845ba39a..4fb675e31f0e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUI.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUI.java @@ -25,6 +25,7 @@ import android.os.UserHandle; import android.os.UserManager; import com.android.systemui.SystemUI; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.statusbar.CommandQueue; import java.io.FileDescriptor; @@ -98,6 +99,18 @@ public class PipUI extends SystemUI implements CommandQueue.Callbacks { mPipManager.setShelfHeight(visible, height); } + public void setPinnedStackAnimationType(int animationType) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationType(animationType); + } + } + + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (mPipManager != null) { + mPipManager.setPinnedStackAnimationListener(listener); + } + } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mPipManager == null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index cb94e28e3467..e98dec0835bf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -41,6 +41,8 @@ import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; @@ -59,12 +61,11 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states for Phones. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; private Context mContext; private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; private Handler mHandler = new Handler(); private final PinnedStackListener mPinnedStackListener = new PipManagerPinnedStackListener(); @@ -78,7 +79,9 @@ public class PipManager implements BasePipManager { private PipMenuActivityController mMenuController; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; + private PipTaskOrganizer mPipTaskOrganizer; private PipAppOpsListener mAppOpsListener; + private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener; /** * Handler for display rotation changes. @@ -124,20 +127,6 @@ public class PipManager implements BasePipManager { } @Override - public void onPinnedStackAnimationStarted() { - // Disable touches while the animation is running - mTouchHandler.setTouchEnabled(false); - } - - @Override - public void onPinnedStackAnimationEnded() { - // Re-enable touches after the animation completes - mTouchHandler.setTouchEnabled(true); - mTouchHandler.onPinnedStackAnimationEnded(); - mMenuController.onPinnedStackAnimationEnded(); - } - - @Override public void onPinnedActivityRestartAttempt(boolean clearedTask) { mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); } @@ -149,10 +138,7 @@ public class PipManager implements BasePipManager { private class PipManagerPinnedStackListener extends PinnedStackListener { @Override public void onListenerRegistered(IPinnedStackController controller) { - mHandler.post(() -> { - mPipBoundsHandler.setPinnedStackController(controller); - mTouchHandler.setPinnedStackController(controller); - }); + mHandler.post(() -> mTouchHandler.setPinnedStackController(controller)); } @Override @@ -164,18 +150,9 @@ public class PipManager implements BasePipManager { } @Override - public void onMinimizedStateChanged(boolean isMinimized) { - mHandler.post(() -> { - mPipBoundsHandler.onMinimizedStateChanged(isMinimized); - mTouchHandler.setMinimizedState(isMinimized, true /* fromController */); - }); - } - - @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> updateMovementBounds(animatingBounds, fromImeAdjustment, - fromShelfAdjustment)); + false /* fromShelfAdjustment */)); } @Override @@ -207,7 +184,10 @@ public class PipManager implements BasePipManager { @Override public void onDisplayInfoChanged(DisplayInfo displayInfo) { - mHandler.post(() -> mPipBoundsHandler.onDisplayInfoChanged(displayInfo)); + mHandler.post(() -> { + mPipBoundsHandler.onDisplayInfoChanged(displayInfo); + mPipTaskOrganizer.onDisplayInfoChanged(displayInfo); + }); } @Override @@ -219,13 +199,6 @@ public class PipManager implements BasePipManager { public void onAspectRatioChanged(float aspectRatio) { mHandler.post(() -> mPipBoundsHandler.onAspectRatioChanged(aspectRatio)); } - - @Override - public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect bounds) { - mHandler.post(() -> { - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, aspectRatio, bounds); - }); - } } @Inject @@ -234,7 +207,6 @@ public class PipManager implements BasePipManager { FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mActivityManager = ActivityManager.getService(); - mActivityTaskManager = ActivityTaskManager.getService(); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); @@ -243,24 +215,29 @@ public class PipManager implements BasePipManager { } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); - mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController, + mMenuController = new PipMenuActivityController(context, mMediaController, mInputConsumerController); - mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager, - mMenuController, mInputConsumerController, mPipBoundsHandler, + mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager, + mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); - // If SystemUI restart, and it already existed a pinned stack, - // register the pip input consumer to ensure touch can send to it. try { - ActivityManager.StackInfo stackInfo = mActivityTaskManager.getStackInfo( + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); + ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { + // If SystemUI restart, and it already existed a pinned stack, + // register the pip input consumer to ensure touch can send to it. mInputConsumerController.registerInputConsumer(); } } catch (RemoteException e) { @@ -320,6 +297,46 @@ public class PipManager implements BasePipManager { }); } + @Override + public void setPinnedStackAnimationType(int animationType) { + mHandler.post(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener); + } + + @Override + public void onPipTransitionStarted() { + // Disable touches while the animation is running + mTouchHandler.setTouchEnabled(false); + if (mPinnedStackAnimationRecentsListener != null) { + try { + mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to callback recents", e); + } + } + } + + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + // Re-enable touches after the animation completes + mTouchHandler.setTouchEnabled(true); + mTouchHandler.onPinnedStackAnimationEnded(); + mMenuController.onPinnedStackAnimationEnded(); + } + private void updateMovementBounds(Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment) { // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler first. diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index c7bfc06829b3..d660b670446b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; import android.app.ActivityTaskManager; -import android.app.IActivityManager; import android.app.RemoteAction; import android.content.Context; import android.content.Intent; @@ -68,11 +67,8 @@ public class PipMenuActivityController { public static final int MESSAGE_MENU_STATE_CHANGED = 100; public static final int MESSAGE_EXPAND_PIP = 101; - public static final int MESSAGE_MINIMIZE_PIP = 102; public static final int MESSAGE_DISMISS_PIP = 103; public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104; - public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105; - public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106; public static final int MESSAGE_SHOW_MENU = 107; public static final int MENU_STATE_NONE = 0; @@ -100,11 +96,6 @@ public class PipMenuActivityController { void onPipExpand(); /** - * Called when the PIP requested to be minimized. - */ - void onPipMinimize(); - - /** * Called when the PIP requested to be dismissed. */ void onPipDismiss(); @@ -116,7 +107,6 @@ public class PipMenuActivityController { } private Context mContext; - private IActivityManager mActivityManager; private PipMediaController mMediaController; private InputConsumerController mInputConsumerController; @@ -146,10 +136,6 @@ public class PipMenuActivityController { mListeners.forEach(l -> l.onPipExpand()); break; } - case MESSAGE_MINIMIZE_PIP: { - mListeners.forEach(l -> l.onPipMinimize()); - break; - } case MESSAGE_DISMISS_PIP: { mListeners.forEach(l -> l.onPipDismiss()); break; @@ -194,10 +180,9 @@ public class PipMenuActivityController { } }; - public PipMenuActivityController(Context context, IActivityManager activityManager, + public PipMenuActivityController(Context context, PipMediaController mediaController, InputConsumerController inputConsumerController) { mContext = context; - mActivityManager = activityManager; mMediaController = mediaController; mInputConsumerController = inputConsumerController; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index c6e28522ccbd..91f539c3a13d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -22,7 +22,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; -import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.Context; import android.graphics.Point; @@ -39,7 +38,9 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.internal.os.SomeArgs; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -65,8 +66,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; - // The fraction of the stack width that the user has to drag offscreen to minimize the PiP - private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f; // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f; @@ -74,10 +73,10 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private static final int MSG_RESIZE_ANIMATE = 2; private static final int MSG_OFFSET_ANIMATE = 3; - private Context mContext; - private IActivityManager mActivityManager; - private IActivityTaskManager mActivityTaskManager; - private Handler mHandler; + private final Context mContext; + private final IActivityTaskManager mActivityTaskManager; + private final PipTaskOrganizer mPipTaskOrganizer; + private final Handler mHandler; private PipMenuActivityController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; @@ -139,14 +138,14 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - public PipMotionHelper(Context context, IActivityManager activityManager, - IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, + public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager, + PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils, FloatingContentCoordinator floatingContentCoordinator) { mContext = context; mHandler = new Handler(ForegroundThread.get().getLooper(), this); - mActivityManager = activityManager; mActivityTaskManager = activityTaskManager; + mPipTaskOrganizer = pipTaskOrganizer; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFlingAnimationUtils = flingAnimationUtils; @@ -285,35 +284,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the closest minimized PiP bounds. - */ - Rect getClosestMinimizedBounds(Rect stackBounds) { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds); - mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets); - return toBounds; - } - - /** - * @return whether the PiP at the current bounds should be minimized. - */ - boolean shouldMinimizePip() { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - if (mBounds.left < 0) { - float offscreenFraction = (float) -mBounds.left / mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else if (mBounds.right > displaySize.x) { - float offscreenFraction = (float) (mBounds.right - displaySize.x) / - mBounds.width(); - return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION; - } else { - return false; - } - } - - /** * @return whether the PiP at the current bounds should be dismissed. */ boolean shouldDismissPip() { @@ -328,25 +298,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * Animates the PiP to the minimized state, slightly offscreen. - */ - void animateToClosestMinimizedState(@Nullable Runnable updateAction) { - final Rect toBounds = getClosestMinimizedBounds(mBounds); - - mAnimatedBounds.set(mBounds); - mAnimatedBoundsPhysicsAnimator - .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) - .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig); - - if (updateAction != null) { - mAnimatedBoundsPhysicsAnimator.addUpdateListener( - (target, values) -> updateAction.run()); - } - - startBoundsAnimation(); - } - - /** * Flings the PiP to the closest snap target. */ void flingToSnapTarget( @@ -436,8 +387,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call * Animates the PiP from the expanded state to the normal state after the menu is hidden. */ void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction, - Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized, - boolean immediate) { + Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) { if (savedSnapFraction < 0f) { // If there are no saved snap fractions, then just use the current bounds savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), @@ -445,10 +395,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction); - if (minimized) { - normalBounds = getClosestMinimizedBounds(normalBounds); - } - if (immediate) { movePip(normalBounds); } else { @@ -503,6 +449,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call cancelAnimations(); mAnimatedBoundsPhysicsAnimator + .withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds)) .addUpdateListener(mResizePipVsyncUpdateListener) .start(); } @@ -594,13 +541,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call } /** - * @return the distance between points {@param p1} and {@param p2}. - */ - private float distanceBetweenRectOffsets(Rect r1, Rect r2) { - return PointF.length(r1.left - r2.left, r1.top - r2.top); - } - - /** * Handles messages to be processed on the background thread. */ public boolean handleMessage(Message msg) { @@ -608,13 +548,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call case MSG_RESIZE_IMMEDIATE: { SomeArgs args = (SomeArgs) msg.obj; Rect toBounds = (Rect) args.arg1; - try { - mActivityTaskManager.resizePinnedStack( - toBounds, null /* tempPinnedTaskBounds */); - mBounds.set(toBounds); - } catch (RemoteException e) { - Log.e(TAG, "Could not resize pinned stack to bounds: " + toBounds, e); - } + mPipTaskOrganizer.resizePinnedStack(toBounds, PipAnimationController.DURATION_NONE); + mBounds.set(toBounds); return true; } @@ -631,8 +566,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds, - duration); + mPipTaskOrganizer.resizePinnedStack(toBounds, duration); mBounds.set(toBounds); } catch (RemoteException e) { Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e); @@ -654,8 +588,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return true; } - mActivityTaskManager.offsetPinnedStackBounds(stackInfo.stackId, originalBounds, - 0/* xOffset */, offset, duration); + mPipTaskOrganizer.offsetPinnedStack(originalBounds, + 0 /* xOffset */, offset, duration); Rect toBounds = new Rect(originalBounds); toBounds.offset(0, offset); mBounds.set(toBounds); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 8e588e67861c..79a25b2269f6 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -45,6 +45,7 @@ import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.systemui.R; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; @@ -58,8 +59,6 @@ import java.io.PrintWriter; public class PipTouchHandler { private static final String TAG = "PipTouchHandler"; - // Allow the PIP to be dragged to the edge of the screen to be minimized. - private static final boolean ENABLE_MINIMIZE = false; // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed. private static final boolean ENABLE_FLING_DISMISS = false; @@ -67,12 +66,9 @@ public class PipTouchHandler { private static final int BOTTOM_OFFSET_BUFFER_DP = 1; // Allow dragging the PIP to a location to close it - private final boolean mEnableDimissDragToEdge; + private final boolean mEnableDismissDragToEdge; private final Context mContext; private final IActivityManager mActivityManager; - private final IActivityTaskManager mActivityTaskManager; - private final ViewConfiguration mViewConfig; - private final PipMenuListener mMenuListener = new PipMenuListener(); private final PipBoundsHandler mPipBoundsHandler; private final PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; @@ -104,7 +100,7 @@ public class PipTouchHandler { private Runnable mShowDismissAffordance = new Runnable() { @Override public void run() { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.showDismissTarget(); } } @@ -112,7 +108,6 @@ public class PipTouchHandler { // Behaviour states private int mMenuState = MENU_STATE_NONE; - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; private int mImeOffset; @@ -121,7 +116,6 @@ public class PipTouchHandler { private int mMovementBoundsExtraOffsets; private float mSavedSnapFraction = -1f; private boolean mSendingHoverAccessibilityEvents; - private boolean mMovementWithinMinimize; private boolean mMovementWithinDismiss; private PipAccessibilityInteractionConnection mConnection; @@ -146,15 +140,7 @@ public class PipTouchHandler { @Override public void onPipExpand() { - if (!mIsMinimized) { - mMotionHelper.expandPip(); - } - } - - @Override - public void onPipMinimize() { - setMinimizedStateInternal(true); - mMotionHelper.animateToClosestMinimizedState(null /* updateAction */); + mMotionHelper.expandPip(); } @Override @@ -175,26 +161,24 @@ public class PipTouchHandler { IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, InputConsumerController inputConsumerController, PipBoundsHandler pipBoundsHandler, + PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator) { - // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; - mActivityTaskManager = activityTaskManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); - mViewConfig = ViewConfiguration.get(context); mMenuController = menuController; - mMenuController.addListener(mMenuListener); + mMenuController.addListener(new PipMenuListener()); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(), 2.5f); mGesture = new DefaultPipTouchGesture(); - mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager, + mMotionHelper = new PipMotionHelper(mContext, activityTaskManager, pipTaskOrganizer, mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper); - mTouchState = new PipTouchState(mViewConfig, mHandler, + mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); @@ -203,7 +187,7 @@ public class PipTouchHandler { R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); + mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); // Register the listener for input consumer touch events inputConsumerController.setInputListener(this::handleTouchEvent); @@ -339,8 +323,7 @@ public class PipTouchHandler { // If we have a deferred resize, apply it now if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) { mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - true /* immediate */); + mNormalMovementBounds, mMovementBounds, true /* immediate */); mSavedSnapFraction = -1f; mDeferResizeToNormalBoundsUntilRotation = -1; } @@ -482,44 +465,6 @@ public class PipTouchHandler { } /** - * Sets the minimized state. - */ - private void setMinimizedStateInternal(boolean isMinimized) { - if (!ENABLE_MINIMIZE) { - return; - } - setMinimizedState(isMinimized, false /* fromController */); - } - - /** - * Sets the minimized state. - */ - void setMinimizedState(boolean isMinimized, boolean fromController) { - if (!ENABLE_MINIMIZE) { - return; - } - if (mIsMinimized != isMinimized) { - MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, - isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager)); - } - mIsMinimized = isMinimized; - mSnapAlgorithm.setMinimized(isMinimized); - - if (fromController) { - if (isMinimized) { - // Move the PiP to the new bounds immediately if minimized - mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds)); - } - } else if (mPinnedStackController != null) { - try { - mPinnedStackController.setIsMinimized(isMinimized); - } catch (RemoteException e) { - Log.e(TAG, "Could not set minimized state", e); - } - } - } - - /** * Sets the menu visibility. */ private void setMenuState(int menuState, boolean resize) { @@ -562,8 +507,7 @@ public class PipTouchHandler { if (mDeferResizeToNormalBoundsUntilRotation == -1) { Rect normalBounds = new Rect(mNormalBounds); mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction, - mNormalMovementBounds, mMovementBounds, mIsMinimized, - false /* immediate */); + mNormalMovementBounds, mMovementBounds, false /* immediate */); mSavedSnapFraction = -1f; } } else { @@ -601,8 +545,6 @@ public class PipTouchHandler { * Gesture controlling normal movement of the PIP. */ private class DefaultPipTouchGesture extends PipTouchGesture { - // Whether the PiP was on the left side of the screen at the start of the gesture - private boolean mStartedOnLeft; private final Point mStartPosition = new Point(); private final PointF mDelta = new PointF(); @@ -615,17 +557,15 @@ public class PipTouchHandler { Rect bounds = mMotionHelper.getBounds(); mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); - mStartedOnLeft = bounds.left < mMovementBounds.centerX(); - mMovementWithinMinimize = true; mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; - // If the menu is still visible, and we aren't minimized, then just poke the menu + // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it - if (mMenuState != MENU_STATE_NONE && !mIsMinimized) { + if (mMenuState != MENU_STATE_NONE) { mMenuController.pokeMenu(); } - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mDismissViewController.createDismissTarget(); mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY); } @@ -640,7 +580,7 @@ public class PipTouchHandler { if (touchState.startedDragging()) { mSavedSnapFraction = -1f; - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { mHandler.removeCallbacks(mShowDismissAffordance); mDismissViewController.showDismissTarget(); } @@ -662,17 +602,11 @@ public class PipTouchHandler { mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { updateDismissFraction(); } final PointF curPos = touchState.getLastTouchPosition(); - if (mMovementWithinMinimize) { - // Track if movement remains near starting edge to identify swipes to minimize - mMovementWithinMinimize = mStartedOnLeft - ? curPos.x <= mMovementBounds.left + mTmpBounds.width() - : curPos.x >= mMovementBounds.right; - } if (mMovementWithinDismiss) { // Track if movement remains near the bottom edge to identify swipe to dismiss mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom; @@ -684,7 +618,7 @@ public class PipTouchHandler { @Override public boolean onUp(PipTouchState touchState) { - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Clean up the dismiss target regardless of the touch state in case the touch // enabled state changes while the user is interacting cleanUpDismissTarget(); @@ -704,7 +638,7 @@ public class PipTouchHandler { vel.y, isFling); final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal && (mMovementWithinDismiss || isUpWithinDimiss); - if (mEnableDimissDragToEdge) { + if (mEnableDismissDragToEdge) { // Check if the user dragged or flung the PiP offscreen to dismiss it if (mMotionHelper.shouldDismissPip() || isFlingToBot) { MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, @@ -717,33 +651,10 @@ public class PipTouchHandler { } if (touchState.isDragging()) { - final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize - && (mStartedOnLeft ? vel.x < 0 : vel.x > 0); - if (ENABLE_MINIMIZE && - !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) { - // Pip should be minimized - setMinimizedStateInternal(true); - if (mMenuState == MENU_STATE_FULL) { - // If the user dragged the expanded PiP to the edge, then hiding the menu - // will trigger the PiP to be scaled back to the normal size with the - // minimize offset adjusted - mMenuController.hideMenu(); - } else { - mMotionHelper.animateToClosestMinimizedState( - PipTouchHandler.this::updateDismissFraction /* updateAction */); - } - return true; - } - if (mIsMinimized) { - // If we're dragging and it wasn't a minimize gesture then we shouldn't be - // minimized. - setMinimizedStateInternal(false); - } - Runnable endAction = null; if (mMenuState != MENU_STATE_NONE) { - // If the menu is still visible, and we aren't minimized, then just poke the - // menu so that it will timeout after the user stops touching it + // If the menu is still visible, then just poke the menu so that + // it will timeout after the user stops touching it mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } else { @@ -759,10 +670,6 @@ public class PipTouchHandler { } else { mMotionHelper.animateToClosestSnapTarget(); } - } else if (mIsMinimized) { - // This was a tap, so no longer minimized - mMotionHelper.animateToClosestSnapTarget(); - setMinimizedStateInternal(false); } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap mMotionHelper.expandPip(); @@ -821,14 +728,12 @@ public class PipTouchHandler { pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds); pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds); pw.println(innerPrefix + "mMenuState=" + mMenuState); - pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized); pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); - pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge); - pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE); + pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); mSnapAlgorithm.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); mMotionHelper.dump(pw, innerPrefix); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 487c2533b0bb..cb1a218af954 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -50,7 +50,9 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.pip.BasePipManager; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; +import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; @@ -66,7 +68,7 @@ import javax.inject.Singleton; * Manages the picture-in-picture (PIP) UI and states. */ @Singleton -public class PipManager implements BasePipManager { +public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipManager"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -91,7 +93,6 @@ public class PipManager implements BasePipManager { private static final int INVALID_RESOURCE_TYPE = -1; public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1; - public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2; /** * PIPed activity is playing a media and it can be paused. @@ -112,6 +113,7 @@ public class PipManager implements BasePipManager { private Context mContext; private PipBoundsHandler mPipBoundsHandler; + private PipTaskOrganizer mPipTaskOrganizer; private IActivityTaskManager mActivityTaskManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; @@ -205,8 +207,7 @@ public class PipManager implements BasePipManager { } @Override - public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + public void onMovementBoundsChanged(Rect animatingBounds, boolean fromImeAdjustment) { mHandler.post(() -> { // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first. mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds, @@ -234,6 +235,8 @@ public class PipManager implements BasePipManager { mInitialized = true; mContext = context; mPipBoundsHandler = new PipBoundsHandler(context); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler); + mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); IntentFilter intentFilter = new IntentFilter(); @@ -279,9 +282,12 @@ public class PipManager implements BasePipManager { mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); + mPipTaskOrganizer.registerPipTransitionCallback(this); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); + ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer( + mPipTaskOrganizer, WINDOWING_MODE_PINNED); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -422,20 +428,13 @@ public class PipManager implements BasePipManager { case STATE_PIP_MENU: mCurrentPipBounds = mMenuModePipBounds; break; - case STATE_PIP: - mCurrentPipBounds = mPipBounds; - break; + case STATE_PIP: // fallthrough default: mCurrentPipBounds = mPipBounds; break; } - try { - int animationDurationMs = -1; - mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds, - animationDurationMs); - } catch (RemoteException e) { - Log.e(TAG, "resizeStack failed", e); - } + mPipTaskOrganizer.resizePinnedStack( + mCurrentPipBounds, PipAnimationController.DURATION_DEFAULT_MS); } /** @@ -449,13 +448,6 @@ public class PipManager implements BasePipManager { } /** - * Returns the default PIP bound. - */ - public Rect getPipBounds() { - return mPipBounds; - } - - /** * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}. */ @@ -692,18 +684,27 @@ public class PipManager implements BasePipManager { // If PIPed activity is launched again by Launcher or intent, make it fullscreen. movePipToFullscreen(); } + }; - @Override - public void onPinnedStackAnimationEnded() { - if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()"); + @Override + public void onPipTransitionStarted() { } - switch (getState()) { - case STATE_PIP_MENU: - showPipMenu(); - break; - } + @Override + public void onPipTransitionFinished() { + onPipTransitionFinishedOrCanceled(); + } + + @Override + public void onPipTransitionCanceled() { + onPipTransitionFinishedOrCanceled(); + } + + private void onPipTransitionFinishedOrCanceled() { + if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()"); + if (getState() == STATE_PIP_MENU) { + showPipMenu(); } - }; + } /** * A listener interface to receive notification on changes in PIP. diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 34cad51e1c9f..1cd63881a700 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -61,9 +61,11 @@ import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.Dumpable; import com.android.systemui.model.SysUiState; +import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipUI; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.shared.recents.IOverviewProxy; +import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; @@ -388,6 +390,32 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + @Override + public void notifySwipeToHomeFinished() { + if (!verifyCaller("notifySwipeToHomeFinished")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { + if (!verifyCaller("setPinnedStackAnimationListener")) { + return; + } + long token = Binder.clearCallingIdentity(); + try { + mPipUI.setPinnedStackAnimationListener(listener); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private boolean verifyCaller(String reason) { final int callerId = Binder.getCallingUserHandle().getIdentifier(); if (callerId != mCurrentBoundedUserId) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java new file mode 100644 index 000000000000..6c09a46833a2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.IWindowContainer; +import android.view.SurfaceControl; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Unit tests against {@link PipAnimationController} to ensure that it sends the right callbacks + * depending on the various interactions. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipAnimationControllerTest extends SysuiTestCase { + + private PipAnimationController mPipAnimationController; + + @Mock + private IWindowContainer mWindowContainer; + + @Mock + private PipAnimationController.PipAnimationCallback mPipAnimationCallback; + + @Before + public void setUp() throws Exception { + mPipAnimationController = new PipAnimationController(mContext); + MockitoAnnotations.initMocks(this); + } + + @Test + public void getAnimator_withAlpha_returnFloatAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + + assertEquals("Expect ANIM_TYPE_ALPHA animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA); + } + + @Test + public void getAnimator_withBounds_returnBoundsAnimator() { + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), new Rect()); + + assertEquals("Expect ANIM_TYPE_BOUNDS animation", + animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS); + } + + @Test + public void getAnimator_whenSameTypeRunning_updateExistingAnimator() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + oldAnimator.start(); + + final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue2); + + assertEquals("getAnimator with same type returns same animator", + oldAnimator, newAnimator); + assertEquals("getAnimator with same type updates end value", + endValue2, newAnimator.getEndValue()); + } + + @Test + public void getAnimator_scheduleFinishPip() { + PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip()); + + animator = mPipAnimationController + .getAnimator(mWindowContainer, false /* scheduleFinishPip */, + new Rect(), 0f, 1f); + assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip()); + } + + @Test + public void pipTransitionAnimator_updateEndValue() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue1 = new Rect(100, 100, 200, 200); + final Rect endValue2 = new Rect(200, 200, 300, 300); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue1); + + animator.updateEndValue(endValue2); + + assertEquals("updateEndValue updates end value", animator.getEndValue(), endValue2); + } + + @Test + public void pipTransitionAnimator_setPipAnimationCallback() { + final Rect startValue = new Rect(0, 0, 100, 100); + final Rect endValue = new Rect(100, 100, 200, 200); + final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController + .getAnimator(mWindowContainer, true /* scheduleFinishPip */, + startValue, endValue); + animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new); + + animator.setPipAnimationCallback(mPipAnimationCallback); + + // onAnimationStart triggers onPipAnimationStart + animator.onAnimationStart(animator); + verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator); + + // onAnimationCancel triggers onPipAnimationCancel + animator.onAnimationCancel(animator); + verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator); + + // onAnimationEnd triggers onPipAnimationEnd + animator.onAnimationEnd(animator); + verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer), + any(SurfaceControl.Transaction.class), eq(animator)); + } + + /** + * A dummy {@link SurfaceControl.Transaction} class. + * This is created as {@link Mock} does not support method chaining. + */ + private static class DummySurfaceControlTx extends SurfaceControl.Transaction { + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) { + return this; + } + + @Override + public void apply() {} + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java index bc3ce8baddee..1dbcf10d08d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java @@ -16,13 +16,7 @@ package com.android.systemui.pip; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; import android.content.ComponentName; import android.graphics.Rect; @@ -31,7 +25,6 @@ import android.testing.TestableLooper; import android.testing.TestableResources; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IPinnedStackController; import androidx.test.filters.SmallTest; @@ -40,9 +33,6 @@ import com.android.systemui.SysuiTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; /** * Unit tests against {@link PipBoundsHandler}, including but not limited to: @@ -55,22 +45,18 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class PipBoundsHandlerTest extends SysuiTestCase { private static final int ROUNDING_ERROR_MARGIN = 10; + private static final float DEFAULT_ASPECT_RATIO = 1f; + private static final Rect EMPTY_CURRENT_BOUNDS = null; private PipBoundsHandler mPipBoundsHandler; private DisplayInfo mDefaultDisplayInfo; - private Rect mDefaultDisplayRect; - - @Mock - private IPinnedStackController mPinnedStackController; @Before public void setUp() throws Exception { mPipBoundsHandler = new PipBoundsHandler(mContext); - MockitoAnnotations.initMocks(this); initializeMockResources(); mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo); - mPipBoundsHandler.setPinnedStackController(mPinnedStackController); } private void initializeMockResources() { @@ -94,142 +80,80 @@ public class PipBoundsHandlerTest extends SysuiTestCase { mDefaultDisplayInfo.displayId = 1; mDefaultDisplayInfo.logicalWidth = 1000; mDefaultDisplayInfo.logicalHeight = 1500; - mDefaultDisplayRect = new Rect(0, 0, - mDefaultDisplayInfo.logicalWidth, mDefaultDisplayInfo.logicalHeight); } @Test - public void setShelfHeight_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void setShelfHeight_offsetBounds() { final int shelfHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.setShelfHeight(true, shelfHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -shelfHeight); - assertBoundsWithMargin("PiP bounds offset by shelf height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -shelfHeight); + assertBoundsWithMargin("PiP bounds offset by shelf height", oldPosition, newPosition); } @Test - public void onImeVisibilityChanged_offsetBounds() throws Exception { - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); + public void onImeVisibilityChanged_offsetBounds() { final int imeHeight = 100; - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - lastPosition.offset(0, -imeHeight); - assertBoundsWithMargin("PiP bounds offset by IME height", - lastPosition, destinationBounds.getValue()); + oldPosition.offset(0, -imeHeight); + assertBoundsWithMargin("PiP bounds offset by IME height", oldPosition, newPosition); } @Test - public void onPrepareAnimation_startAnimation() throws Exception { - final Rect sourceRectHint = new Rect(100, 100, 200, 200); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(sourceRectHint, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), eq(sourceRectHint), anyInt()); - final Rect capturedDestinationBounds = destinationBounds.getValue(); - assertFalse("Destination bounds is not empty", - capturedDestinationBounds.isEmpty()); - assertBoundsWithMargin("Destination bounds within Display", - mDefaultDisplayRect, capturedDestinationBounds); - } - - @Test - public void onSaveReentryBounds_restoreLastPosition() throws Exception { + public void onSaveReentryBounds_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect lastPosition = destinationBounds.getValue(); - lastPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(componentName, lastPosition); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); + oldPosition.offset(0, -100); + mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - assertBoundsWithMargin("Last position is restored", - lastPosition, destinationBounds.getValue()); + assertBoundsWithMargin("Last position is restored", oldPosition, newPosition); } @Test - public void onResetReentryBounds_componentMatch_useDefaultBounds() throws Exception { + public void onResetReentryBounds_componentMatch_useDefaultBounds() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(componentName); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Use default bounds", defaultBounds, actualBounds); } @Test - public void onResetReentryBounds_componentMismatch_restoreLastPosition() throws Exception { + public void onResetReentryBounds_componentMismatch_restoreLastPosition() { final ComponentName componentName = new ComponentName(mContext, "component1"); - final ArgumentCaptor<Rect> destinationBounds = ArgumentCaptor.forClass(Rect.class); - - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect defaultBounds = new Rect(destinationBounds.getValue()); + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); final Rect newBounds = new Rect(defaultBounds); newBounds.offset(0, -100); mPipBoundsHandler.onSaveReentryBounds(componentName, newBounds); - // Reset the pinned stack controller since we will do another verify later on - reset(mPinnedStackController); mPipBoundsHandler.onResetReentryBounds(new ComponentName(mContext, "component2")); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - verify(mPinnedStackController).startAnimation( - destinationBounds.capture(), isNull(), anyInt()); - final Rect actualBounds = destinationBounds.getValue(); assertBoundsWithMargin("Last position is restored", newBounds, actualBounds); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index c7f5f630ca1a..bf797291380a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -2570,8 +2570,6 @@ final class ActivityManagerShellCommand extends ShellCommand { switch (op) { case "move-task": return runStackMoveTask(pw); - case "resize-animated": - return runStackResizeAnimated(pw); case "resize-docked-stack": return runStackResizeDocked(pw); case "positiontask": @@ -2648,23 +2646,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } - int runStackResizeAnimated(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); - final Rect bounds; - if ("null".equals(peekNextArg())) { - bounds = null; - } else { - bounds = getBounds(); - if (bounds == null) { - getErrPrintWriter().println("Error: invalid input bounds"); - return -1; - } - } - mTaskInterface.animateResizePinnedStack(stackId, bounds, -1); - return 0; - } - int runStackResizeDocked(PrintWriter pw) throws RemoteException { final Rect bounds = getBounds(); final Rect taskBounds = getBounds(); @@ -3285,8 +3266,6 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" move-task <TASK_ID> <STACK_ID> [true|false]"); pw.println(" Move <TASK_ID> from its current stack to the top (true) or"); pw.println(" bottom (false) of <STACK_ID>."); - pw.println(" resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>"); - pw.println(" Same as resize, but allow animation."); pw.println(" resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]"); pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>"); pw.println(" and supplying temporary different task bounds indicated by"); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 190af7a06c8d..d715ed408d51 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3845,13 +3845,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mDisplayContent.setLayoutNeeded(); } mWmService.mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); - - // Notify the pinned stack upon all windows drawn. If there was an animation in - // progress then this signal will resume that animation. - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - if (pinnedStack != null) { - pinnedStack.onAllWindowsDrawn(); - } } } } @@ -6755,18 +6748,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // for the next re-entry into PiP (assuming the activity is not hidden or destroyed) final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); if (pinnedStack == null) return; - final Rect stackBounds; - if (pinnedStack.lastAnimatingBoundsWasToFullscreen()) { - // We are animating the bounds, use the pre-animation bounds to save the snap - // fraction - stackBounds = pinnedStack.mPreAnimationBounds; - } else { - // We skip the animation if the fullscreen configuration is not compatible, so - // use the current bounds to calculate the saved snap fraction instead - // (see PinnedActivityStack.skipResizeAnimation()) - stackBounds = mTmpRect; - pinnedStack.getBounds(stackBounds); - } + final Rect stackBounds = mTmpRect; + pinnedStack.getBounds(stackBounds); mDisplayContent.mPinnedStackControllerLocked.saveReentryBounds( mActivityComponent, stackBounds); } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 2f1cc0532ccc..ff890ff13ee3 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -98,10 +98,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.TaskProto.ACTIVITIES; import static com.android.server.wm.TaskProto.ACTIVITY_TYPE; @@ -202,7 +198,7 @@ import java.util.function.Consumer; /** * State and management of a single stack of activities. */ -class ActivityStack extends Task implements BoundsAnimationTarget { +class ActivityStack extends Task { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM; static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -327,11 +323,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // Set when an animation has been requested but has not yet started from the UI thread. This is // cleared when the animation actually starts. private boolean mBoundsAnimatingRequested = false; - private boolean mBoundsAnimatingToFullscreen = false; - private boolean mCancelCurrentBoundsAnimation = false; private Rect mBoundsAnimationTarget = new Rect(); private Rect mBoundsAnimationSourceHintBounds = new Rect(); - private @BoundsAnimationController.AnimationType int mAnimationType; Rect mPreAnimationBounds = new Rect(); @@ -790,17 +783,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, false /* creating */); - if (windowingMode == WINDOWING_MODE_PINNED) { - // This stack will be visible before SystemUI requests PiP animation to start, and if - // the selected animation type is fade in, this stack will be close first. If the - // animation does not start early, user may see this full screen window appear again - // after the closing transition finish, which could cause screen to flicker. - // Check if animation should start here in advance. - final BoundsAnimationController controller = getDisplay().mBoundsAnimationController; - if (controller.isAnimationTypeFadeIn()) { - setPinnedStackAlpha(0f); - } - } } /** @@ -905,6 +887,9 @@ class ActivityStack extends Task implements BoundsAnimationTarget { likelyResolvedMode = parent != null ? parent.getWindowingMode() : WINDOWING_MODE_FULLSCREEN; } + if (currentMode == WINDOWING_MODE_PINNED) { + mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); + } if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN && topActivity != null && !topActivity.noDisplay && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) { @@ -3480,91 +3465,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } } - void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, - boolean fromFullscreen) { - if (!inPinnedWindowingMode()) return; - - /** - * TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - * If this PIP Task is controlled by a TaskOrganizer, the animation occurs entirely - * on the TaskOrganizer side, so we just hand over the leash without doing any animation. - * We have to be careful to not schedule the enter-pip callback as the TaskOrganizer - * needs to have flexibility to schedule that at an appropriate point in the animation. - */ - if (isControlledByTaskOrganizer()) return; - if (toBounds == null /* toFullscreen */) { - final Configuration parentConfig = getParent().getConfiguration(); - final ActivityRecord top = topRunningNonOverlayTaskActivity(); - if (top != null && !top.isConfigurationCompatible(parentConfig)) { - // The final orientation of this activity will change after moving to full screen. - // Start freezing screen here to prevent showing a temporary full screen window. - top.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT); - dismissPip(); - return; - } - } - - // Get the from-bounds - final Rect fromBounds = new Rect(); - getBounds(fromBounds); - - // Get non-null fullscreen to-bounds for animating if the bounds are null - @BoundsAnimationController.SchedulePipModeChangedState int schedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - final boolean toFullscreen = toBounds == null; - if (toFullscreen) { - if (fromFullscreen) { - throw new IllegalArgumentException("Should not defer scheduling PiP mode" - + " change on animation to fullscreen."); - } - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; - - mWmService.getStackBounds( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); - if (!mTmpToBounds.isEmpty()) { - // If there is a fullscreen bounds, use that - toBounds = new Rect(mTmpToBounds); - } else { - // Otherwise, use the display bounds - toBounds = new Rect(); - getDisplayContent().getBounds(toBounds); - } - } else if (fromFullscreen) { - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); - - final Rect finalToBounds = toBounds; - final @BoundsAnimationController.SchedulePipModeChangedState int - finalSchedulePipModeChangedState = schedulePipModeChangedState; - final DisplayContent displayContent = getDisplayContent(); - @BoundsAnimationController.AnimationType int intendedAnimationType = - displayContent.mBoundsAnimationController.getAnimationType(); - if (intendedAnimationType == FADE_IN) { - if (fromFullscreen) { - setPinnedStackAlpha(0f); - } - if (toBounds.width() == fromBounds.width() - && toBounds.height() == fromBounds.height()) { - intendedAnimationType = BoundsAnimationController.BOUNDS; - } else if (!fromFullscreen && !toBounds.equals(fromBounds)) { - // intendedAnimationType may have been reset at the end of RecentsAnimation, - // force it to BOUNDS type if we know for certain we're animating to - // a different bounds, especially for expand and collapse of PiP window. - intendedAnimationType = BoundsAnimationController.BOUNDS; - } - } - - final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; - mCancelCurrentBoundsAnimation = false; - displayContent.mBoundsAnimationController.getHandler().post(() -> { - displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, - finalToBounds, animationDuration, finalSchedulePipModeChangedState, - fromFullscreen, toFullscreen, animationType); - }); - } - void dismissPip() { if (!isActivityTypeStandardOrUndefined()) { throw new IllegalArgumentException( @@ -3593,21 +3493,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { }); } - private void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds, - boolean forceUpdate) { - // It is guaranteed that the activities requiring the update will be in the pinned stack at - // this point (either reparented before the animation into PiP, or before reparenting after - // the animation out of PiP) - if (!isAttached()) { - return; - } - final PooledConsumer c = PooledLambda.obtainConsumer( - ActivityStackSupervisor::updatePictureInPictureMode, mStackSupervisor, - PooledLambda.__(Task.class), targetStackBounds, forceUpdate); - forAllLeafTasks(c, true /* traverseTopToBottom */); - c.recycle(); - } - void prepareFreezingTaskBounds() { forAllLeafTasks(Task::prepareFreezingBounds, true /* traverseTopToBottom */); } @@ -3704,34 +3589,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * Sets the bounds animation target bounds ahead of an animation. This can't currently be done - * in onAnimationStart() since that is started on the UiThread. - */ - private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, - boolean toFullscreen) { - if (mAnimationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = true; - } - mBoundsAnimatingToFullscreen = toFullscreen; - if (destBounds != null) { - mBoundsAnimationTarget.set(destBounds); - } else { - mBoundsAnimationTarget.setEmpty(); - } - if (sourceHintBounds != null) { - mBoundsAnimationSourceHintBounds.set(sourceHintBounds); - } else if (!mBoundsAnimating) { - // If the bounds are already animating, we don't want to reset the source hint. This is - // because the source hint is sent when starting the animation from the client that - // requested to enter pip. Other requests can adjust the pip bounds during an animation, - // but could accidentally reset the source hint bounds. - mBoundsAnimationSourceHintBounds.setEmpty(); - } - - mPreAnimationBounds.set(getRawBounds()); - } - - /** * @return the final bounds for the bounds animation. */ void getFinalAnimationBounds(Rect outBounds) { @@ -3763,30 +3620,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } /** - * Reset the current animation running on {@link #mBoundsAnimationTarget}. - * - * @param destinationBounds the final destination bounds - */ - void resetCurrentBoundsAnimation(Rect destinationBounds) { - boolean animating = (mBoundsAnimatingRequested || mBoundsAnimating) - && !mBoundsAnimationTarget.isEmpty(); - - // The final boundary is updated while there is an existing boundary animation. Let's - // cancel this animation to prevent the obsolete animation overwritten updated bounds. - if (animating && !destinationBounds.equals(mBoundsAnimationTarget)) { - final BoundsAnimationController controller = - getDisplayContent().mBoundsAnimationController; - controller.getHandler().post(() -> controller.cancel(this)); - } - // Once we've set the bounds based on the rotation of the old bounds in the new - // orientation, clear the animation target bounds since they are obsolete, and - // cancel any currently running animations - mBoundsAnimationTarget.setEmpty(); - mBoundsAnimationSourceHintBounds.setEmpty(); - mCancelCurrentBoundsAnimation = true; - } - - /** * Updates the passed-in {@code inOutBounds} based on the current state of the * docked controller. This gets run *after* the override configuration is updated, so it's * safe to rely on the controller's state in here (though eventually this dependence should @@ -4590,110 +4423,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return task != null; } - public boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (mCancelCurrentBoundsAnimation) { - return false; - } - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - - return true; - } - - void onAllWindowsDrawn() { - if (!mBoundsAnimating && !mBoundsAnimatingRequested) { - return; - } - - getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn(); - } - - @Override // AnimatesBounds - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Don't run the animation if the stack is already detached - return false; - } - - if (animationType == BoundsAnimationController.BOUNDS) { - mBoundsAnimatingRequested = false; - mBoundsAnimating = true; - } - mAnimationType = animationType; - - // If we are changing UI mode, as in the PiP to fullscreen - // transition, then we need to wait for the window to draw. - if (schedulePipModeChangedCallback) { - forAllWindows((w) -> { - w.mWinAnimator.resetDrawState(); - }, false /* traverseTopToBottom */); - } - } - - if (inPinnedWindowingMode()) { - try { - mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); - } catch (RemoteException e) { - // I don't believe you... - } - - if ((schedulePipModeChangedCallback || animationType == FADE_IN)) { - // We need to schedule the PiP mode change before the animation up. It is possible - // in this case for the animation down to not have been completed, so always - // force-schedule and update to the client to ensure that it is notified that it - // is no longer in picture-in-picture mode - updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate); - } - } - return true; - } - - @Override // AnimatesBounds - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen) { - synchronized (mWmService.mGlobalLock) { - if (inPinnedWindowingMode()) { - // Update to the final bounds if requested. This is done here instead of in the - // bounds animator to allow us to coordinate this after we notify the PiP mode - // changed - - if (schedulePipModeChangedCallback) { - // We need to schedule the PiP mode change after the animation down, so use the - // final bounds - updatePictureInPictureModeForPinnedStackAnimation(mBoundsAnimationTarget, - false /* forceUpdate */); - } - - if (mAnimationType == BoundsAnimationController.FADE_IN) { - setPinnedStackAlpha(1f); - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - return; - } - - if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { - setPinnedStackSize(finalStackSize, null); - } else { - // We have been canceled, so the final stack size is null, still run the - // animation-end logic - onPipAnimationEndResize(); - } - - mWmService.mAtmService.notifyPinnedStackAnimationEnded(); - if (moveToFullscreen) { - ((ActivityStack) this).dismissPip(); - } - } else { - // No PiP animation, just run the normal animation-end logic - onPipAnimationEndResize(); - } - } - } - /** * Sets the current picture-in-picture aspect ratio. */ @@ -4741,42 +4470,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { getDisplayContent().getPinnedStackController().setActions(actions); } - /** Called immediately prior to resizing the tasks at the end of the pinned stack animation. */ - void onPipAnimationEndResize() { - mBoundsAnimating = false; - forAllLeafTasks(Task::clearPreserveNonFloatingState, false /* traverseTopToBottom */); - mWmService.requestTraversal(); - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - synchronized (mWmService.mGlobalLock) { - if (!isAttached()) { - // Unnecessary to pause the animation because the stack is detached. - return false; - } - - // Workaround for the recents animation -- normally we need to wait for the new activity - // to show before starting the PiP animation, but because we start and show the home - // activity early for the recents animation prior to the PiP animation starting, there - // is no subsequent all-drawn signal. In this case, we can skip the pause when the home - // stack is already visible and drawn. - final ActivityStack homeStack = mDisplayContent.getRootHomeTask(); - if (homeStack == null) { - return true; - } - final Task homeTask = homeStack.getTopMostTask(); - if (homeTask == null) { - return true; - } - final ActivityRecord homeApp = homeTask.getTopVisibleActivity(); - if (!homeTask.isVisible() || homeApp == null) { - return true; - } - return !homeApp.allDrawn; - } - } - /** * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen * bounds and we have a deferred PiP mode changed callback set with the animation. @@ -4798,25 +4491,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { return mBoundsAnimating; } - public boolean isAnimatingBounds() { - return mBoundsAnimating; - } - - public boolean lastAnimatingBoundsWasToFullscreen() { - return mBoundsAnimatingToFullscreen; - } - - public boolean isAnimatingBoundsToFullscreen() { - return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); - } - - public boolean pinnedStackResizeDisallowed() { - if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { - return true; - } - return false; - } - /** Returns true if a removal action is still being deferred. */ boolean checkCompleteDeferredRemoval() { if (isAnimating(TRANSITION | CHILDREN)) { @@ -4843,21 +4517,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { || activityType == ACTIVITY_TYPE_ASSISTANT; } - @Override - public boolean setPinnedStackAlpha(float alpha) { - // Hold the lock since this is called from the BoundsAnimator running on the UiThread - synchronized (mWmService.mGlobalLock) { - final SurfaceControl sc = getSurfaceControl(); - if (sc == null || !sc.isValid()) { - // If the stack is already removed, don't bother updating any stack animation - return false; - } - getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha); - scheduleAnimation(); - return !mCancelCurrentBoundsAnimation; - } - } - public DisplayInfo getDisplayInfo() { return mDisplayContent.getDisplayInfo(); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 97b638820a8a..b2d2f624e458 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1696,40 +1696,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } - void resizePinnedStack(Rect displayedBounds, Rect inConfigBounds) { - // TODO(multi-display): The display containing the stack should be passed in. - final ActivityStack stack = - mRootWindowContainer.getDefaultDisplay().getRootPinnedTask(); - if (stack == null) { - Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); - return; - } - - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack"); - mService.deferWindowLayout(); - try { - Rect configBounds = null; - if (inConfigBounds != null) { - // Use 0,0 as the position for the inset rect because we are headed for fullscreen. - configBounds = tempRect; - configBounds.top = 0; - configBounds.left = 0; - configBounds.right = inConfigBounds.width(); - configBounds.bottom = inConfigBounds.height(); - } - if (displayedBounds != null && inConfigBounds == null) { - // We have finished the animation into PiP, and are resizing the tasks to match the - // stack bounds, while layouts are deferred, update any task state as a part of - // transitioning it from fullscreen into a floating state. - stack.onPipAnimationEndResize(); - } - stack.resize(displayedBounds, configBounds, !PRESERVE_WINDOWS, !DEFER_RESUME); - } finally { - mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } - } - private void removeStackInSurfaceTransaction(ActivityStack stack) { if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) { /** diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 600a125cdf43..a467e736abcc 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -97,7 +97,6 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -2401,7 +2400,6 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); - updateBounds(mStartActivity.getTask(), mLaunchParams.mBounds); if (DEBUG_TASKS) { Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity @@ -2424,21 +2422,6 @@ class ActivityStarter { mIntentDelivered = true; } - @VisibleForTesting - void updateBounds(Task task, Rect bounds) { - if (bounds.isEmpty()) { - return; - } - - final Task rootTask = task.getRootTask(); - if (rootTask != null && rootTask.inPinnedWindowingMode()) { - mService.animateResizePinnedStack(rootTask.mTaskId, bounds, -1); - } else { - // TODO: I don't believe it is possible to reach this else condition anymore... - task.setBounds(bounds); - } - } - private void addOrReparentStartingActivity(Task parent, String reason) { if (mStartActivity.getTask() == null || mStartActivity.getTask() == parent) { parent.addChild(mStartActivity); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5392257ae0b0..945893986ae3 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2645,64 +2645,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "resizeStack: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - public void offsetPinnedStackBounds(int stackId, Rect compareBounds, int xOffset, int yOffset, - int animationDuration) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "offsetPinnedStackBounds()"); - - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - if (xOffset == 0 && yOffset == 0) { - return; - } - final ActivityStack stack = mRootWindowContainer.getStack(stackId); - if (stack == null) { - Slog.w(TAG, "offsetPinnedStackBounds: stackId " + stackId + " not found."); - return; - } - if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { - throw new IllegalArgumentException("Stack: " + stackId - + " doesn't support animated resize."); - } - final Rect destBounds = new Rect(); - stack.getAnimationOrCurrentBounds(destBounds); - if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) { - Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); - return; - } - destBounds.offset(xOffset, yOffset); - stack.animateResizePinnedStack(destBounds, null /* sourceHintBounds */, - animationDuration, false /* fromFullscreen */); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } /** * Moves the specified task to the primary-split-screen stack. * @@ -4016,17 +3958,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("Stack: " + stack + " doesn't support animated resize."); } - /** - * TODO(b/146594635): Remove all PIP animation code from WM - * once SysUI handles animation. Don't even try to animate TaskOrganized tasks. - */ - if (animate && !stack.isControlledByTaskOrganizer()) { - stack.animateResizePinnedStack(null /* destBounds */, - null /* sourceHintBounds */, animationDuration, - false /* fromFullscreen */); - } else { - stack.dismissPip(); - } + stack.dismissPip(); } } finally { Binder.restoreCallingIdentity(ident); @@ -4121,15 +4053,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } private boolean isInPictureInPictureMode(ActivityRecord r) { - if (r == null || r.getRootTask() == null || !r.inPinnedWindowingMode() - || r.getRootTask().isInStackLocked(r) == null) { - return false; - } - - // If we are animating to fullscreen then we have already dispatched the PIP mode - // changed, so we should reflect that check here as well. - final ActivityStack taskStack = r.getRootTask(); - return !taskStack.isAnimatingBoundsToFullscreen(); + return r != null + && r.getRootTask() != null + && r.inPinnedWindowingMode() + && r.getRootTask().isInStackLocked(r) != null; } @Override @@ -4213,11 +4140,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // if it is not already expanding to fullscreen. Otherwise, the arguments will // be used the next time the activity enters PiP final ActivityStack stack = r.getRootTask(); - if (!stack.isAnimatingBoundsToFullscreen()) { - stack.setPictureInPictureAspectRatio( - r.pictureInPictureArgs.getAspectRatio()); - stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); - } + stack.setPictureInPictureAspectRatio( + r.pictureInPictureArgs.getAspectRatio()); + stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); } logPictureInPictureArgs(params); } @@ -4421,31 +4346,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .supportsLocalVoiceInteraction(); } - /** Notifies all listeners when the pinned stack animation starts. */ - @Override - public void notifyPinnedStackAnimationStarted() { - mTaskChangeNotificationController.notifyPinnedStackAnimationStarted(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - @Override - public void notifyPinnedStackAnimationEnded() { - mTaskChangeNotificationController.notifyPinnedStackAnimationEnded(); - } - - @Override - public void resizePinnedStack(Rect displayedBounds, Rect configBounds) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()"); - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mGlobalLock) { - mStackSupervisor.resizePinnedStack(displayedBounds, configBounds); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - @Override public boolean updateConfiguration(Configuration values) { mAmInternal.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()"); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java deleted file mode 100644 index 5385e2fce008..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; - -import android.animation.AnimationHandler; -import android.animation.Animator; -import android.animation.ValueAnimator; -import android.annotation.IntDef; -import android.content.Context; -import android.graphics.Rect; -import android.os.Debug; -import android.os.Handler; -import android.os.IBinder; -import android.util.ArrayMap; -import android.util.Slog; -import android.view.Choreographer; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.graphics.SfVsyncFrameCallbackProvider; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Enables animating bounds of objects. - * - * In multi-window world bounds of both stack and tasks can change. When we need these bounds to - * change smoothly and not require the app to relaunch (e.g. because it handles resizes and - * relaunching it would cause poorer experience), these class provides a way to directly animate - * the bounds of the resized object. - * - * The object that is resized needs to implement {@link BoundsAnimationTarget} interface. - * - * NOTE: All calls to methods in this class should be done on the Animation thread - */ -public class BoundsAnimationController { - private static final boolean DEBUG_LOCAL = false; - private static final boolean DEBUG = DEBUG_LOCAL || DEBUG_ANIM; - private static final String TAG = TAG_WITH_CLASS_NAME || DEBUG_LOCAL - ? "BoundsAnimationController" : TAG_WM; - private static final int DEBUG_ANIMATION_SLOW_DOWN_FACTOR = 1; - - private static final int DEFAULT_TRANSITION_DURATION = 425; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START, - SCHEDULE_PIP_MODE_CHANGED_ON_END}) - public @interface SchedulePipModeChangedState {} - /** Do not schedule any PiP mode changed callbacks as a part of this animation. */ - public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0; - /** Schedule a PiP mode changed callback when this animation starts. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1; - /** Schedule a PiP mode changed callback when this animation ends. */ - public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2; - - public static final int BOUNDS = 0; - public static final int FADE_IN = 1; - - @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {} - - private static final int FADE_IN_DURATION = 500; - - // Only accessed on UI thread. - private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>(); - - private final class AppTransitionNotifier - extends WindowManagerInternal.AppTransitionListener implements Runnable { - - public void onAppTransitionCancelledLocked() { - if (DEBUG) Slog.d(TAG, "onAppTransitionCancelledLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - public void onAppTransitionFinishedLocked(IBinder token) { - if (DEBUG) Slog.d(TAG, "onAppTransitionFinishedLocked:" - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition); - animationFinished(); - } - private void animationFinished() { - if (mFinishAnimationAfterTransition) { - mHandler.removeCallbacks(this); - // This might end up calling into activity manager which will be bad since we have - // the window manager lock held at this point. Post a message to take care of the - // processing so we don't deadlock. - mHandler.post(this); - } - } - - @Override - public void run() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.onAnimationEnd(null); - } - } - } - - private final Handler mHandler; - private final AppTransition mAppTransition; - private final AppTransitionNotifier mAppTransitionNotifier = new AppTransitionNotifier(); - private final Interpolator mFastOutSlowInInterpolator; - private boolean mFinishAnimationAfterTransition = false; - private final AnimationHandler mAnimationHandler; - private Choreographer mChoreographer; - private @AnimationType int mAnimationType; - - private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; - - BoundsAnimationController(Context context, AppTransition transition, Handler handler, - AnimationHandler animationHandler) { - mHandler = handler; - mAppTransition = transition; - mAppTransition.registerListenerLocked(mAppTransitionNotifier); - mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); - mAnimationHandler = animationHandler; - if (animationHandler != null) { - // If an animation handler is provided, then ensure that it runs on the sf vsync tick - handler.post(() -> { - mChoreographer = Choreographer.getSfInstance(); - animationHandler.setProvider(new SfVsyncFrameCallbackProvider(mChoreographer)); - }); - } - } - - @VisibleForTesting - final class BoundsAnimator extends ValueAnimator - implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener { - - private final BoundsAnimationTarget mTarget; - private final @AnimationType int mAnimationType; - private final Rect mFrom = new Rect(); - private final Rect mTo = new Rect(); - private final Rect mTmpRect = new Rect(); - private final Rect mTmpTaskBounds = new Rect(); - - // True if this this animation was canceled and will be replaced the another animation from - // the same {@link #BoundsAnimationTarget} target. - private boolean mSkipFinalResize; - // True if this animation was canceled by the user, not as a part of a replacing animation - private boolean mSkipAnimationEnd; - - // True if the animation target is animating from the fullscreen. Only one of - // {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be true at any time in the - // animation. - private boolean mMoveFromFullscreen; - // True if the animation target should be moved to the fullscreen stack at the end of this - // animation. Only one of {@link mMoveToFullscreen} or {@link mMoveFromFullscreen} can be - // true at any time in the animation. - private boolean mMoveToFullscreen; - - // Whether to schedule PiP mode changes on animation start/end - private @SchedulePipModeChangedState int mSchedulePipModeChangedState; - private @SchedulePipModeChangedState int mPrevSchedulePipModeChangedState; - - // Depending on whether we are animating from - // a smaller to a larger size - private int mFrozenTaskWidth; - private int mFrozenTaskHeight; - - // Timeout callback to ensure we continue the animation if waiting for resuming or app - // windows drawn fails - private final Runnable mResumeRunnable = () -> { - if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn"); - resume(); - }; - - // If this animator is explicitly cancelled when it's in paused state, we should not - // attempt to resume the animation. Use this flag to avoid such behavior. - private boolean mIsCancelled; - - BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from, - Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, - @SchedulePipModeChangedState int prevShedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { - super(); - mTarget = target; - mAnimationType = animationType; - mFrom.set(from); - mTo.set(to); - mSchedulePipModeChangedState = schedulePipModeChangedState; - mPrevSchedulePipModeChangedState = prevShedulePipModeChangedState; - mMoveFromFullscreen = moveFromFullscreen; - mMoveToFullscreen = moveToFullscreen; - addUpdateListener(this); - addListener(this); - - // If we are animating from smaller to larger, we want to change the task bounds - // to their final size immediately so we can use scaling to make the window - // larger. Likewise if we are going from bigger to smaller, we want to wait until - // the end so we don't have to upscale from the smaller finished size. - if (mAnimationType == BOUNDS) { - if (animatingToLargerSize()) { - mFrozenTaskWidth = mTo.width(); - mFrozenTaskHeight = mTo.height(); - } else { - mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); - mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); - } - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget - + " mPrevSchedulePipModeChangedState=" + mPrevSchedulePipModeChangedState - + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState); - mIsCancelled = false; - mFinishAnimationAfterTransition = false; - mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth, - mFrom.top + mFrozenTaskHeight); - - // Boost the thread priority of the animation thread while the bounds animation is - // running - updateBooster(); - - // Ensure that we have prepared the target for animation before we trigger any size - // changes, so it can swap surfaces in to appropriate modes, or do as it wishes - // otherwise. - boolean continueAnimation; - if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) { - continueAnimation = mTarget.onAnimationStart( - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START, - false /* forceUpdate */, mAnimationType); - - // When starting an animation from fullscreen, pause here and wait for the - // windows-drawn signal before we start the rest of the transition down into PiP. - if (continueAnimation && mMoveFromFullscreen - && mTarget.shouldDeferStartOnMoveToFullscreen()) { - pause(); - } - } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END && - mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - // We are replacing a running animation into PiP, but since it hasn't completed, the - // client will not currently receive any picture-in-picture mode change callbacks. - // However, we still need to report to them that they are leaving PiP, so this will - // force an update via a mode changed callback. - continueAnimation = mTarget.onAnimationStart( - true /* schedulePipModeChangedCallback */, true /* forceUpdate */, - mAnimationType); - } else { - // The animation is already running, but we should check that the TaskStack is still - // valid before continuing with the animation - continueAnimation = mTarget.isAttached(); - } - - if (!continueAnimation) { - // No point of trying to animate something that isn't attached to the hierarchy - // anymore. - cancel(); - return; - } - - // Immediately update the task bounds if they have to become larger, but preserve - // the starting position so we don't jump at the beginning of the animation. - if (animatingToLargerSize()) { - mTarget.setPinnedStackSize(mFrom, mTmpRect); - - // We pause the animation until the app has drawn at the new size. - // The target will notify us via BoundsAnimationController#resume. - // We do this here and pause the animation, rather than just defer starting it - // so we can enter the animating state and have WindowStateAnimator apply the - // correct logic to make this resize seamless. - if (mMoveToFullscreen) { - pause(); - } - } - } - - @Override - public void pause() { - if (DEBUG) Slog.d(TAG, "pause: waiting for windows drawn"); - super.pause(); - mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); - } - - @Override - public void resume() { - if (DEBUG) Slog.d(TAG, "resume:"); - mHandler.removeCallbacks(mResumeRunnable); - if (!mIsCancelled) super.resume(); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - final float value = (Float) animation.getAnimatedValue(); - if (mAnimationType == FADE_IN) { - if (!mTarget.setPinnedStackAlpha(value)) { - cancelAndCallAnimationEnd(); - } - return; - } - - final float remains = 1 - value; - mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f); - mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f); - mTmpRect.right = (int) (mFrom.right * remains + mTo.right * value + 0.5f); - mTmpRect.bottom = (int) (mFrom.bottom * remains + mTo.bottom * value + 0.5f); - if (DEBUG) Slog.d(TAG, "animateUpdate: mTarget=" + mTarget + " mBounds=" - + mTmpRect + " from=" + mFrom + " mTo=" + mTo + " value=" + value - + " remains=" + remains); - - mTmpTaskBounds.set(mTmpRect.left, mTmpRect.top, - mTmpRect.left + mFrozenTaskWidth, mTmpRect.top + mFrozenTaskHeight); - - if (!mTarget.setPinnedStackSize(mTmpRect, mTmpTaskBounds)) { - // Whoops, the target doesn't feel like animating anymore. Let's immediately finish - // any further animation. - if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled"); - - // If we have already scheduled a PiP mode changed at the start of the animation, - // then we need to clean up and schedule one at the end, since we have canceled the - // animation to the final state. - if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - - // Since we are cancelling immediately without a replacement animation, send the - // animation end to maintain callback parity, but also skip any further resizes - cancelAndCallAnimationEnd(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " mSkipFinalResize=" + mSkipFinalResize - + " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition - + " mAppTransitionIsRunning=" + mAppTransition.isRunning() - + " callers=" + Debug.getCallers(2)); - - // There could be another animation running. For example in the - // move to fullscreen case, recents will also be closing while the - // previous task will be taking its place in the fullscreen stack. - // we have to ensure this is completed before we finish the animation - // and take our place in the fullscreen stack. - if (mAppTransition.isRunning() && !mFinishAnimationAfterTransition) { - mFinishAnimationAfterTransition = true; - return; - } - - if (!mSkipAnimationEnd) { - // If this animation has already scheduled the picture-in-picture mode on start, and - // we are not skipping the final resize due to being canceled, then move the PiP to - // fullscreen once the animation ends - if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget - + " moveToFullscreen=" + mMoveToFullscreen); - mTarget.onAnimationEnd(mSchedulePipModeChangedState == - SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null, - mMoveToFullscreen); - } - - // Clean up this animation - removeListener(this); - removeUpdateListener(this); - mRunningAnimations.remove(mTarget); - - // Reset the thread priority of the animation thread after the bounds animation is done - updateBooster(); - } - - @Override - public void onAnimationCancel(Animator animation) { - mIsCancelled = true; - // Always skip the final resize when the animation is canceled - mSkipFinalResize = true; - mMoveToFullscreen = false; - } - - private void cancelAndCallAnimationEnd() { - if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget); - mSkipAnimationEnd = false; - super.cancel(); - } - - @Override - public void cancel() { - if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget); - mSkipAnimationEnd = true; - super.cancel(); - - // Reset the thread priority of the animation thread if the bounds animation is canceled - updateBooster(); - } - - /** - * @return true if the animation target is the same as the input bounds. - */ - boolean isAnimatingTo(Rect bounds) { - return mTo.equals(bounds); - } - - /** - * @return true if we are animating to a larger surface size - */ - @VisibleForTesting - boolean animatingToLargerSize() { - // TODO: Fix this check for aspect ratio changes - return (mFrom.width() * mFrom.height() < mTo.width() * mTo.height()); - } - - @Override - public void onAnimationRepeat(Animator animation) { - // Do nothing - } - - @Override - public AnimationHandler getAnimationHandler() { - if (mAnimationHandler != null) { - return mAnimationHandler; - } - return super.getAnimationHandler(); - } - } - - public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, animationType); - } - - /** - * Cancel existing animation if the destination was modified. - */ - void cancel(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - if (existing != null) { - // Cancel animation. Since its already started, send animation end to client. - if (DEBUG) Slog.d(TAG, "cancel: mTarget= " + target); - existing.cancelAndCallAnimationEnd(); - } - } - - @VisibleForTesting - BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to, - int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen, - @AnimationType int animationType) { - final BoundsAnimator existing = mRunningAnimations.get(target); - - if (isRunningFadeInAnimation(target) && from.width() == to.width() - && from.height() == to.height()) { - animationType = FADE_IN; - } - final boolean replacing = existing != null; - @SchedulePipModeChangedState int prevSchedulePipModeChangedState = - NO_PIP_MODE_CHANGED_CALLBACKS; - - if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to - + " schedulePipModeChangedState=" + schedulePipModeChangedState - + " replacing=" + replacing); - - Rect frozenTask = new Rect(); - if (replacing) { - if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen) - && (!moveFromFullscreen || existing.mMoveFromFullscreen)) { - // Just let the current animation complete if it has the same destination as the - // one we are trying to start, and, if moveTo/FromFullscreen was requested, already - // has that flag set. - if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as " - + "existing=" + existing + ", ignoring..."); - return existing; - } - - // Save the previous state - prevSchedulePipModeChangedState = existing.mSchedulePipModeChangedState; - - // Update the PiP callback states if we are replacing the animation - if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep" - + " existing deferred state"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback" - + " on start already processed, schedule deferred update on end"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) { - if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) { - if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled," - + " callback on start will be processed"); - } else { - if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep" - + " existing deferred state"); - schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; - } - } - - // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation - // specifies a direction. - if (!moveFromFullscreen && !moveToFullscreen) { - moveToFullscreen = existing.mMoveToFullscreen; - moveFromFullscreen = existing.mMoveFromFullscreen; - } - - // We are in the middle of an existing animation, so that this new animation may - // start from an interpolated bounds. We should keep using the existing frozen task - // width/height for consistent configurations. - frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight); - - // Since we are replacing, we skip both animation start and end callbacks - existing.cancel(); - } - if (animationType == FADE_IN) { - target.setPinnedStackSize(to, null); - } - - final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to, - schedulePipModeChangedState, prevSchedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen, frozenTask); - mRunningAnimations.put(target, animator); - animator.setFloatValues(0f, 1f); - animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION - : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION) - * DEBUG_ANIMATION_SLOW_DOWN_FACTOR); - animator.setInterpolator(mFastOutSlowInInterpolator); - animator.start(); - return animator; - } - - public void setAnimationType(@AnimationType int animationType) { - mAnimationType = animationType; - } - - /** return the current animation type. */ - public @AnimationType int getAnimationType() { - @AnimationType int animationType = mAnimationType; - // Default to BOUNDS. - mAnimationType = BOUNDS; - return animationType; - } - - boolean isAnimationTypeFadeIn() { - return mAnimationType == FADE_IN; - } - - public Handler getHandler() { - return mHandler; - } - - public void onAllWindowsDrawn() { - if (DEBUG) Slog.d(TAG, "onAllWindowsDrawn:"); - mHandler.post(this::resume); - } - - private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) { - final BoundsAnimator existing = mRunningAnimations.get(target); - return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted(); - } - - private void resume() { - for (int i = 0; i < mRunningAnimations.size(); i++) { - final BoundsAnimator b = mRunningAnimations.valueAt(i); - b.resume(); - } - } - - private void updateBooster() { - WindowManagerService.sThreadPriorityBooster.setBoundsAnimationRunning( - !mRunningAnimations.isEmpty()); - } -} diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java deleted file mode 100644 index b1d535904fab..000000000000 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.graphics.Rect; - -/** - * The target for a BoundsAnimation. - * @see BoundsAnimationController - */ -interface BoundsAnimationTarget { - - /** - * Callback for the target to inform it that the animation has started, so it can do some - * necessary preparation. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @return whether to continue the animation - */ - boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, - @BoundsAnimationController.AnimationType int animationType); - - /** - * @return Whether the animation should be paused waiting for the windows to draw before - * entering PiP. - */ - boolean shouldDeferStartOnMoveToFullscreen(); - - /** - * Sets the size of the target (without any intermediate steps, like scheduling animation) - * but freezes the bounds of any tasks in the target at taskBounds, to allow for more - * flexibility during resizing. Only works for the pinned stack at the moment. This will - * only be called between onAnimationStart() and onAnimationEnd(). - * - * @return Whether the target should continue to be animated and this call was successful. - * If false, the animation will be cancelled because the user has determined that the - * animation is now invalid and not required. In such a case, the cancel will trigger the - * animation end callback as well, but will not send any further size changes. - */ - boolean setPinnedStackSize(Rect displayedBounds, Rect configBounds); - - /** Sets the alpha of the animation target */ - boolean setPinnedStackAlpha(float alpha); - - /** - * Callback for the target to inform it that the animation has ended, so it can do some - * necessary cleanup. - * - * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed - * callbacks - * @param finalStackSize the final stack bounds to set on the target (can be to indicate that - * the animation was cancelled and the target does not need to update to the final stack bounds) - * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack - * when the animation completes - */ - void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, - boolean moveToFullscreen); - - /** @return True if the target is attached to the window hierarchy. */ - default boolean isAttached() { - return true; - } -} diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0029dc8c3c25..8ccb59f39081 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -150,7 +150,6 @@ import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.utils.RegionUtils.forEachRectReverse; import static com.android.server.wm.utils.RegionUtils.rectListToRegion; -import android.animation.AnimationHandler; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -317,7 +316,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>(); final ArraySet<ActivityRecord> mChangingApps = new ArraySet<>(); final UnknownAppVisibilityController mUnknownAppVisibilityController; - BoundsAnimationController mBoundsAnimationController; private MetricsLogger mMetricsLogger; @@ -977,10 +975,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAppTransitionController = new AppTransitionController(mWmService, this); mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this); - AnimationHandler animationHandler = new AnimationHandler(); - mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext, - mAppTransition, mWmService.mAnimationHandler, animationHandler); - final InputChannel inputChannel = mWmService.mInputManager.monitorInput( "PointerEventDispatcher" + mDisplayId, mDisplayId); mPointerEventDispatcher = new PointerEventDispatcher(inputChannel); diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 668b6095738f..e14b8ae13bbc 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -16,8 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -52,7 +50,7 @@ import java.util.List; * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio * and IME state into account. * 2) When rotating the device: the controller calculates the new bounds in the new orientation, - * taking the minimized and IME state into account. In this case, we currently ignore the + * taking the IME state into account. In this case, we currently ignore the * SystemUI adjustments (ie. expanded for menu, interaction, etc). * * Other changes in the system, including adjustment of IME, configuration change, and more are @@ -72,8 +70,6 @@ class PinnedStackController { private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); - // States that affect how the PIP can be manipulated - private boolean mIsMinimized; private boolean mIsImeShowing; private int mImeHeight; @@ -84,9 +80,6 @@ class PinnedStackController { // Used to calculate stack bounds across rotations private final DisplayInfo mDisplayInfo = new DisplayInfo(); - // The size and position information that describes where the pinned stack will go by default. - private float mDefaultAspectRatio; - // The aspect ratio bounds of the PIP. private float mMinAspectRatio; private float mMaxAspectRatio; @@ -98,43 +91,12 @@ class PinnedStackController { * The callback object passed to listeners for them to notify the controller of state changes. */ private class PinnedStackControllerCallback extends IPinnedStackController.Stub { - - @Override - public void setIsMinimized(final boolean isMinimized) { - mHandler.post(() -> { - mIsMinimized = isMinimized; - }); - } - @Override public int getDisplayRotation() { synchronized (mService.mGlobalLock) { return mDisplayInfo.rotation; } } - - @Override - public void startAnimation(Rect destinationBounds, Rect sourceRectHint, - int animationDuration) { - synchronized (mService.mGlobalLock) { - final ActivityStack pinnedStack = mDisplayContent.getRootPinnedTask(); - pinnedStack.animateResizePinnedStack(destinationBounds, - sourceRectHint, animationDuration, true /* fromFullscreen */); - } - } - - @Override - public void resetBoundsAnimation(Rect bounds) { - synchronized (mService.mGlobalLock) { - if (mDisplayContent.hasPinnedTask()) { - final ActivityStack pinnedStack = mDisplayContent.getTopStackInWindowingMode( - WINDOWING_MODE_PINNED); - if (pinnedStack != null) { - pinnedStack.resetCurrentBoundsAnimation(bounds); - } - } - } - } } /** @@ -157,10 +119,6 @@ class PinnedStackController { mDisplayContent = displayContent; mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); reloadResources(); - // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload - // resources as it would clobber mAspectRatio when entering PiP from fullscreen which - // triggers a configuration change and the resources to be reloaded. - mAspectRatio = mDefaultAspectRatio; } void onConfigurationChanged() { @@ -172,8 +130,6 @@ class PinnedStackController { */ private void reloadResources() { final Resources res = mService.mContext.getResources(); - mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics); mMinAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); @@ -191,12 +147,8 @@ class PinnedStackController { mPinnedStackListener = listener; notifyDisplayInfoChanged(mDisplayInfo); notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); - // The movement bounds notification needs to be sent before the minimized state, since - // SystemUI may use the bounds to restore the minimized position - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); notifyActionsChanged(mActions); - notifyMinimizeChanged(mIsMinimized); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } @@ -247,8 +199,7 @@ class PinnedStackController { void onDisplayInfoChanged(DisplayInfo displayInfo) { synchronized (mService.mGlobalLock) { setDisplayInfo(displayInfo); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -269,7 +220,7 @@ class PinnedStackController { mIsImeShowing = imeShowing; mImeHeight = imeHeight; notifyImeVisibilityChanged(imeShowing, imeHeight); - notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */); + notifyMovementBoundsChanged(true /* fromImeAdjustment */); } /** @@ -279,10 +230,7 @@ class PinnedStackController { if (Float.compare(mAspectRatio, aspectRatio) != 0) { mAspectRatio = aspectRatio; notifyAspectRatioChanged(aspectRatio); - notifyMovementBoundsChanged(false /* fromImeAdjustment */, - false /* fromShelfAdjustment */); - notifyPrepareAnimation(null /* sourceHintRect */, aspectRatio, - null /* stackBounds */); + notifyMovementBoundsChanged(false /* fromImeAdjustment */); } } @@ -304,10 +252,6 @@ class PinnedStackController { notifyActionsChanged(mActions); } - void prepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - notifyPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } - /** * Notifies listeners that the PIP needs to be adjusted for the IME. */ @@ -331,19 +275,6 @@ class PinnedStackController { } /** - * Notifies listeners that the PIP minimized state has changed. - */ - private void notifyMinimizeChanged(boolean isMinimized) { - if (mPinnedStackListener != null) { - try { - mPinnedStackListener.onMinimizedStateChanged(isMinimized); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering minimize changed event.", e); - } - } - } - - /** * Notifies listeners that the PIP actions have changed. */ private void notifyActionsChanged(List<RemoteAction> actions) { @@ -359,8 +290,7 @@ class PinnedStackController { /** * Notifies listeners that the PIP movement bounds have changed. */ - private void notifyMovementBoundsChanged(boolean fromImeAdjustment, - boolean fromShelfAdjustment) { + private void notifyMovementBoundsChanged(boolean fromImeAdjustment) { synchronized (mService.mGlobalLock) { if (mPinnedStackListener == null) { return; @@ -371,8 +301,7 @@ class PinnedStackController { if (pinnedStack != null) { pinnedStack.getAnimationOrCurrentBounds(animatingBounds); } - mPinnedStackListener.onMovementBoundsChanged(animatingBounds, - fromImeAdjustment, fromShelfAdjustment); + mPinnedStackListener.onMovementBoundsChanged(animatingBounds, fromImeAdjustment); } catch (RemoteException e) { Slog.e(TAG_WM, "Error delivering actions changed event.", e); } @@ -391,24 +320,10 @@ class PinnedStackController { } } - /** - * Notifies listeners that the PIP animation is about to happen. - */ - private void notifyPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) { - if (mPinnedStackListener == null) return; - try { - mPinnedStackListener.onPrepareAnimation(sourceRectHint, aspectRatio, stackBounds); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering prepare animation event.", e); - } - } - void dump(String prefix, PrintWriter pw) { pw.println(prefix + "PinnedStackController"); - pw.println(prefix + " mDefaultAspectRatio=" + mDefaultAspectRatio); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); pw.println(prefix + " mImeHeight=" + mImeHeight); - pw.println(prefix + " mIsMinimized=" + mIsMinimized); pw.println(prefix + " mAspectRatio=" + mAspectRatio); pw.println(prefix + " mMinAspectRatio=" + mMinAspectRatio); pw.println(prefix + " mMaxAspectRatio=" + mMaxAspectRatio); diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index e92bbaae71d3..adafdec40d37 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -26,8 +26,6 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -426,11 +424,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, return; } - final DisplayContent dc = - mService.mRootWindowContainer.getDefaultDisplay().mDisplayContent; - dc.mBoundsAnimationController.setAnimationType( - controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS); - // We defer canceling the recents animation until the next app transition in the following // cases: // 1) The next launching task is not being animated by the recents animation diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6b6b94681879..4399d6519363 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -26,7 +26,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; @@ -231,7 +230,6 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks.onAnimationFinished(moveHomeToTop ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint); - mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -309,14 +307,6 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - @Deprecated - public void setCancelWithDeferredScreenshot(boolean screenshot) { - synchronized (mService.mGlobalLock) { - setDeferredCancel(true /* deferred */, screenshot); - } - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { synchronized (mService.mGlobalLock) { setDeferredCancel(defer, screenshot); diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index aa6bdfd1aa5b..64d7db26cd7e 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2149,19 +2149,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mService.continueWindowLayout(); } - // TODO(b/146594635): Remove all PIP animation code from WM once SysUI handles animation. - // Notify the pinned stack controller to prepare the PiP animation, expect callback - // delivered from SystemUI to WM to start the animation. Unless we are using - // the TaskOrganizer in which case the animation will be entirely handled - // on that side. - if (mService.mTaskOrganizerController.getTaskOrganizer(WINDOWING_MODE_PINNED) - == null) { - final PinnedStackController pinnedStackController = - display.mDisplayContent.getPinnedStackController(); - pinnedStackController.prepareAnimation(sourceHintBounds, aspectRatio, - null /* stackBounds */); - } - // TODO: revisit the following statement after the animation is moved from WM to SysUI. // Update the visibility of all activities after the they have been reparented to the new // stack. This MUST run after the animation above is scheduled to ensure that the windows diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ecfac1bc7b6e..76805e9f6342 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -111,7 +111,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.drawable.Icon; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; @@ -120,7 +119,6 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; -import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Slog; import android.view.DisplayInfo; @@ -2910,8 +2908,7 @@ class Task extends WindowContainer<WindowContainer> { * we will have a jump at the end. */ boolean isFloating() { - return getWindowConfiguration().tasksAreFloating() - && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState; + return getWindowConfiguration().tasksAreFloating() && !mPreserveNonFloatingState; } /** diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index f715d8f3529b..96a9127344e8 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -37,7 +37,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_MSG = 2; private static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 3; private static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 4; - private static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 5; private static final int NOTIFY_FORCED_RESIZABLE_MSG = 6; private static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 7; private static final int NOTIFY_TASK_ADDED_LISTENERS_MSG = 8; @@ -48,7 +47,6 @@ class TaskChangeNotificationController { private static final int NOTIFY_TASK_REMOVAL_STARTED_LISTENERS = 13; private static final int NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG = 14; private static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15; - private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; @@ -124,14 +122,6 @@ class TaskChangeNotificationController { l.onPinnedActivityRestartAttempt(m.arg1 != 0); }; - private final TaskStackConsumer mNotifyPinnedStackAnimationStarted = (l, m) -> { - l.onPinnedStackAnimationStarted(); - }; - - private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> { - l.onPinnedStackAnimationEnded(); - }; - private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> { l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2); }; @@ -233,12 +223,6 @@ class TaskChangeNotificationController { case NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG: forAllRemoteListeners(mNotifyPinnedActivityRestartAttempt, msg); break; - case NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationStarted, msg); - break; - case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: - forAllRemoteListeners(mNotifyPinnedStackAnimationEnded, msg); - break; case NOTIFY_FORCED_RESIZABLE_MSG: forAllRemoteListeners(mNotifyActivityForcedResizable, msg); break; @@ -386,24 +370,6 @@ class TaskChangeNotificationController { msg.sendToTarget(); } - /** Notifies all listeners when the pinned stack animation starts. */ - void notifyPinnedStackAnimationStarted() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationStarted, msg); - msg.sendToTarget(); - } - - /** Notifies all listeners when the pinned stack animation ends. */ - void notifyPinnedStackAnimationEnded() { - mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - final Message msg = - mHandler.obtainMessage(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG); - forAllLocalListeners(mNotifyPinnedStackAnimationEnded, msg); - msg.sendToTarget(); - } - void notifyActivityDismissingDockedStack() { mHandler.removeMessages(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 6caa27c7fa6f..0f1e623a38f1 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -306,7 +306,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub task.fillTaskInfo(mTmpTaskInfo); boolean changed = lastInfo == null || mTmpTaskInfo.topActivityType != lastInfo.topActivityType - || mTmpTaskInfo.isResizable() != lastInfo.isResizable(); + || mTmpTaskInfo.isResizable() != lastInfo.isResizable() + || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams; if (!(changed || force)) { return; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b25008373a7b..37597fbc7c45 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1078,15 +1078,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } final ActivityStack stack = getRootTask(); - if (inPinnedWindowingMode() && stack != null - && stack.lastAnimatingBoundsWasToFullscreen()) { - // PIP edge case: When going from pinned to fullscreen, we apply a - // tempInsetFrame for the full task - but we're still at the start of the animation. - // To prevent a jump if there's a letterbox, restrict to the parent frame. - mInsetFrame.intersectUnchecked(windowFrames.mParentFrame); - windowFrames.mContainingFrame.intersectUnchecked(windowFrames.mParentFrame); - } - layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame); windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame); layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left; @@ -1343,16 +1334,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } - final Task task = getTask(); - // In the case of stack bound animations, the window frames will update (unlike other - // animations which just modify various transformation properties). We don't want to - // notify the client of frame changes in this case. Not only is it a lot of churn, but - // the frame may not correspond to the surface size or the onscreen area at various - // phases in the animation, and the client will become sad and confused. - if (task != null && task.getStack().isAnimatingBounds()) { - return; - } - boolean didFrameInsetsChange = setReportResizeHints(); boolean configChanged = !isLastConfigReportedToClient(); if (DEBUG_CONFIGURATION && configChanged) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a1a9af680e92..81d0e3e88c1a 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -902,15 +902,9 @@ class WindowStateAnimator { boolean allowStretching = false; task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds); // If we don't have source bounds, we can attempt to use the content insets - // in the following scenario: - // 1. We have content insets. - // 2. We are not transitioning to full screen - // We have to be careful to check "lastAnimatingBoundsWasToFullscreen" rather than - // the mBoundsAnimating state, as we may have already left it and only be here - // because of the force-scale until resize state. + // if we have content insets. if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0 - || mWin.mLastRelayoutContentInsets.height() > 0) - && !task.getStack().lastAnimatingBoundsWasToFullscreen()) { + || mWin.mLastRelayoutContentInsets.height() > 0)) { mTmpSourceBounds.set(task.getStack().mPreAnimationBounds); mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets); allowStretching = true; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index b917e1b92fb9..4fe6d7892cfe 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -31,7 +31,6 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT; @@ -50,7 +49,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -139,39 +137,6 @@ public class ActivityStarterTests extends ActivityTestsBase { } @Test - public void testUpdateLaunchBounds() { - // When in a non-resizeable stack, the task bounds should be updated. - final Task task = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - final Rect bounds = new Rect(10, 10, 100, 100); - - mStarter.updateBounds(task, bounds); - assertEquals(bounds, task.getRequestedOverrideBounds()); - assertEquals(new Rect(), task.getStack().getRequestedOverrideBounds()); - - // When in a resizeable stack, the stack bounds should be updated as well. - final Task task2 = new TaskBuilder(mService.mStackSupervisor) - .setStack(mService.mRootWindowContainer.getDefaultDisplay().createStack( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */)) - .build(); - assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class); - mStarter.updateBounds(task2, bounds); - - verify(mService, times(1)).animateResizePinnedStack(eq(task2.getRootTaskId()), - eq(bounds), anyInt()); - - // In the case of no animation, the stack and task bounds should be set immediately. - if (!ANIMATE) { - assertEquals(bounds, task2.getStack().getRequestedOverrideBounds()); - assertEquals(bounds, task2.getRequestedOverrideBounds()); - } else { - assertEquals(new Rect(), task2.getRequestedOverrideBounds()); - } - } - - @Test public void testStartActivityPreconditions() { verifyStartActivityPreconditions(PRECONDITION_NO_CALLER_APP, START_PERMISSION_DENIED); verifyStartActivityPreconditions(PRECONDITION_NO_INTENT_COMPONENT, diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java deleted file mode 100644 index 1dda535cfb95..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.server.wm.BoundsAnimationController.BOUNDS; -import static com.android.server.wm.BoundsAnimationController.FADE_IN; -import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; -import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; -import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.platform.test.annotations.Presubmit; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.SmallTest; - -import com.android.server.wm.BoundsAnimationController.BoundsAnimator; -import com.android.server.wm.WindowManagerInternal.AppTransitionListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks - * depending on the various interactions. - * - * We are really concerned about only three of the transition states [F = fullscreen, !F = floating] - * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition, - * or if a new animation starts on the same target. The tests below verifies that the target is - * notified of all the cases where it is animating and cancelled so that it can respond - * appropriately. - * - * Build/Install/Run: - * atest WmTests:BoundsAnimationControllerTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class BoundsAnimationControllerTests extends WindowTestsBase { - - /** - * Mock value animator to simulate updates with. - */ - private static class MockValueAnimator extends ValueAnimator { - - private float mFraction; - - MockValueAnimator getWithValue(float fraction) { - mFraction = fraction; - return this; - } - - @Override - public Object getAnimatedValue() { - return mFraction; - } - } - - /** - * Mock app transition to fire notifications to the bounds animator. - */ - private static class MockAppTransition extends AppTransition { - - private AppTransitionListener mListener; - - MockAppTransition(Context context, WindowManagerService wm, DisplayContent displayContent) { - super(context, wm, displayContent); - } - - @Override - void registerListenerLocked(AppTransitionListener listener) { - mListener = listener; - } - - public void notifyTransitionPending() { - mListener.onAppTransitionPendingLocked(); - } - - public void notifyTransitionCancelled(int transit) { - mListener.onAppTransitionCancelledLocked(transit); - } - - public void notifyTransitionStarting(int transit) { - mListener.onAppTransitionStartingLocked(transit, 0, 0, 0); - } - - public void notifyTransitionFinished() { - mListener.onAppTransitionFinishedLocked(null); - } - } - - /** - * A test animate bounds user to track callbacks from the bounds animation. - */ - private static class TestBoundsAnimationTarget implements BoundsAnimationTarget { - - boolean mAwaitingAnimationStart; - boolean mMovedToFullscreen; - boolean mAnimationStarted; - boolean mSchedulePipModeChangedOnStart; - boolean mForcePipModeChangedCallback; - boolean mAnimationEnded; - Rect mAnimationEndFinalStackBounds; - boolean mSchedulePipModeChangedOnEnd; - boolean mBoundsUpdated; - boolean mCancelRequested; - Rect mStackBounds; - Rect mTaskBounds; - float mAlpha; - @BoundsAnimationController.AnimationType int mAnimationType; - - void initialize(Rect from) { - mAwaitingAnimationStart = true; - mMovedToFullscreen = false; - mAnimationStarted = false; - mAnimationEnded = false; - mAnimationEndFinalStackBounds = null; - mForcePipModeChangedCallback = false; - mSchedulePipModeChangedOnStart = false; - mSchedulePipModeChangedOnEnd = false; - mStackBounds = from; - mTaskBounds = null; - mBoundsUpdated = false; - } - - @Override - public boolean onAnimationStart(boolean schedulePipModeChangedCallback, - boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) { - mAwaitingAnimationStart = false; - mAnimationStarted = true; - mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; - mForcePipModeChangedCallback = forceUpdate; - mAnimationType = animationType; - return true; - } - - @Override - public boolean shouldDeferStartOnMoveToFullscreen() { - return true; - } - - @Override - public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { - // TODO: Once we break the runs apart, we should fail() here if this is called outside - // of onAnimationStart() and onAnimationEnd() - if (mCancelRequested) { - mCancelRequested = false; - return false; - } else { - mBoundsUpdated = true; - mStackBounds = stackBounds; - mTaskBounds = taskBounds; - return true; - } - } - - @Override - public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds, - boolean moveToFullscreen) { - mAnimationEnded = true; - mAnimationEndFinalStackBounds = finalStackBounds; - mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback; - mMovedToFullscreen = moveToFullscreen; - mTaskBounds = null; - } - - @Override - public boolean setPinnedStackAlpha(float alpha) { - mAlpha = alpha; - return true; - } - } - - /** - * Drives the animations, makes common assertions along the way. - */ - private static class BoundsAnimationDriver { - - private final BoundsAnimationController mController; - private final TestBoundsAnimationTarget mTarget; - private final MockValueAnimator mMockAnimator; - - private BoundsAnimator mAnimator; - private Rect mFrom; - private Rect mTo; - private Rect mLargerBounds; - private Rect mExpectedFinalBounds; - private @BoundsAnimationController.AnimationType int mAnimationType; - - BoundsAnimationDriver(BoundsAnimationController controller, - TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) { - mController = controller; - mTarget = target; - mMockAnimator = mockValueAnimator; - } - - BoundsAnimationDriver start(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - if (mAnimator != null) { - throw new IllegalArgumentException("Call restart() to restart an animation"); - } - - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - - mTarget.initialize(from); - - // Started, not running - assertTrue(mTarget.mAwaitingAnimationStart); - assertFalse(mTarget.mAnimationStarted); - - startImpl(from, to, animationType); - - // Ensure that the animator is paused for the all windows drawn signal when animating - // to/from fullscreen - if (fromFullscreen || toFullscreen) { - assertTrue(mAnimator.isPaused()); - mController.onAllWindowsDrawn(); - } else { - assertTrue(!mAnimator.isPaused()); - } - - // Started and running - assertFalse(mTarget.mAwaitingAnimationStart); - assertTrue(mTarget.mAnimationStarted); - - return this; - } - - BoundsAnimationDriver restart(Rect to, boolean expectStartedAndPipModeChangedCallback) { - if (mAnimator == null) { - throw new IllegalArgumentException("Call start() to start a new animation"); - } - - BoundsAnimator oldAnimator = mAnimator; - boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo); - - // Reset the animation start state - mTarget.mAnimationStarted = false; - - // Start animation - startImpl(mTarget.mStackBounds, to, BOUNDS); - - if (toSameBounds) { - // Same animator if same final bounds - assertSame(oldAnimator, mAnimator); - } - - if (expectStartedAndPipModeChangedCallback) { - // Replacing animation with pending pip mode changed callback, ensure we update - assertTrue(mTarget.mAnimationStarted); - assertTrue(mTarget.mSchedulePipModeChangedOnStart); - assertTrue(mTarget.mForcePipModeChangedCallback); - } else { - // No animation start for replacing animation - assertFalse(mTarget.mAnimationStarted); - } - mTarget.mAnimationStarted = true; - return this; - } - - private BoundsAnimationDriver startImpl(Rect from, Rect to, - @BoundsAnimationController.AnimationType int animationType) { - boolean fromFullscreen = from.equals(BOUNDS_FULL); - boolean toFullscreen = to.equals(BOUNDS_FULL); - mFrom = new Rect(from); - mTo = new Rect(to); - mExpectedFinalBounds = new Rect(to); - mLargerBounds = getLargerBounds(mFrom, mTo); - mAnimationType = animationType; - - // Start animation - final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_START - : fromFullscreen - ? SCHEDULE_PIP_MODE_CHANGED_ON_END - : NO_PIP_MODE_CHANGED_CALLBACKS; - mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION, - schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType); - - if (animationType == BOUNDS) { - // Original stack bounds, frozen task bounds - assertEquals(mFrom, mTarget.mStackBounds); - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - - // Animating to larger size - if (mFrom.equals(mLargerBounds)) { - assertFalse(mAnimator.animatingToLargerSize()); - } else if (mTo.equals(mLargerBounds)) { - assertTrue(mAnimator.animatingToLargerSize()); - } - } - - return this; - } - - BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) { - // Callback made - assertTrue(mTarget.mAnimationStarted); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart); - return this; - } - - BoundsAnimationDriver update(float t) { - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t)); - - if (mAnimationType == BOUNDS) { - // Temporary stack bounds, frozen task bounds - if (t == 0f) { - assertEquals(mFrom, mTarget.mStackBounds); - } else if (t == 1f) { - assertEquals(mTo, mTarget.mStackBounds); - } else { - assertNotEquals(mFrom, mTarget.mStackBounds); - assertNotEquals(mTo, mTarget.mStackBounds); - } - assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds); - } else { - assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f); - } - return this; - } - - BoundsAnimationDriver cancel() { - // Cancel - mTarget.mCancelRequested = true; - mTarget.mBoundsUpdated = false; - mExpectedFinalBounds = null; - - // Update - mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f)); - - // Not started, not running, cancel reset - assertFalse(mTarget.mCancelRequested); - - // Stack/task bounds not updated - assertFalse(mTarget.mBoundsUpdated); - - // Callback made - assertTrue(mTarget.mAnimationEnded); - assertNull(mTarget.mAnimationEndFinalStackBounds); - - return this; - } - - BoundsAnimationDriver end() { - mAnimator.end(); - - if (mAnimationType == BOUNDS) { - // Final stack bounds - assertEquals(mTo, mTarget.mStackBounds); - assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds); - assertNull(mTarget.mTaskBounds); - } else { - assertEquals(mTarget.mAlpha, 1f, 0.01f); - } - - return this; - } - - BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged, - boolean moveToFullscreen) { - // Callback made - assertTrue(mTarget.mAnimationEnded); - - assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd); - assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen); - return this; - } - - private static Rect getLargerBounds(Rect r1, Rect r2) { - int r1Area = r1.width() * r1.height(); - int r2Area = r2.width() * r2.height(); - return (r1Area <= r2Area) ? r2 : r1; - } - } - - // Constants - private static final boolean SCHEDULE_PIP_MODE_CHANGED = true; - private static final boolean MOVE_TO_FULLSCREEN = true; - private static final int DURATION = 100; - - // Some dummy bounds to represent fullscreen and floating bounds - private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100); - private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95); - private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95); - - // Common - private MockAppTransition mMockAppTransition; - private TestBoundsAnimationTarget mTarget; - private BoundsAnimationController mController; - private BoundsAnimationDriver mDriver; - - // Temp - private static final Rect sTmpRect = new Rect(); - - @Before - public void setUp() throws Exception { - final Context context = getInstrumentation().getTargetContext(); - final Handler handler = new Handler(Looper.getMainLooper()); - mMockAppTransition = new MockAppTransition(context, mWm, mDisplayContent); - mTarget = new TestBoundsAnimationTarget(); - mController = new BoundsAnimationController(context, mMockAppTransition, handler, null); - final MockValueAnimator mockValueAnimator = new MockValueAnimator(); - mDriver = new BoundsAnimationDriver(mController, mTarget, mockValueAnimator); - } - - /** BASE TRANSITIONS **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingTransition() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingTransition() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingTransition() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() { - // When animating from fullscreen and the animation is interruped, we expect the animation - // start callback to be made, with a forced pip mode change callback - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToSameBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS) - .expectStarted(SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .restart(BOUNDS_SMALLER_FLOATING, - false /* expectStartedAndPipModeChangedCallback */) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN); - } - - /** !F->!F w/ CANCEL **/ - - @UiThreadTest - @Test - public void testFloatingToSmallerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFloatingToLargerFloatingCancelFromTarget() { - mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0.25f) - .cancel() - .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - @UiThreadTest - @Test - public void testFadeIn() { - mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN) - .expectStarted(!SCHEDULE_PIP_MODE_CHANGED) - .update(0f) - .update(0.5f) - .update(1f) - .end() - .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN); - } - - /** MISC **/ - - @UiThreadTest - @Test - public void testBoundsAreCopied() { - Rect from = new Rect(0, 0, 100, 100); - Rect to = new Rect(25, 25, 75, 75); - mDriver.start(from, to, BOUNDS) - .update(0.25f) - .end(); - assertEquals(new Rect(0, 0, 100, 100), from); - assertEquals(new Rect(25, 25, 75, 75), to); - } - - /** - * @return whether the task and stack bounds would be the same if they were at the same offset. - */ - private static boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) { - sTmpRect.set(taskBounds); - sTmpRect.offsetTo(stackBounds.left, stackBounds.top); - return stackBounds.equals(sTmpRect); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 444906977f47..d9c5c4c7dffc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1102,12 +1102,8 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect())); assertSecurityException(expectCallable, - () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1)); - assertSecurityException(expectCallable, () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(), new Rect())); - assertSecurityException(expectCallable, - () -> mService.resizePinnedStack(new Rect(), new Rect())); assertSecurityException(expectCallable, () -> mService.getAllStackInfos()); assertSecurityException(expectCallable, () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index c9efb7018616..5c36f5c39e0c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -333,7 +333,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertTrue(mController.isAnimatingTask(activity.getTask())); // Assume activity transition should animate when no - // IRecentsAnimationController#setCancelWithDeferredScreenshot called. + // IRecentsAnimationController#setDeferCancelUntilNextTransition called. assertFalse(mController.shouldDeferCancelWithScreenshot()); assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index b78107e9024f..b3a253029ed0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -208,7 +208,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testSetLaunchTaskBehindOfTargetActivity() { DisplayContent display = mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack homeStack = display.getRootHomeTask(); // Assume the home activity support recents. ActivityRecord targetActivity = homeStack.getTopNonFinishingActivity(); @@ -254,7 +253,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); - display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class); ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mService) |
