From 85cf41fcc3b2a7882355ad9921cc9ca168e4a2b3 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Wed, 15 Jan 2020 15:14:47 -0800 Subject: [PATCH] Move PiP animation to SysUI package The bounds animation is cleaned up within window manager and it's now the SysUI component listening on callbacks from TaskOrganizer for entering to and exiting from PiP mode. Additionally, the expand and move of the PiP window is now part of SysUI as well. Known issues: - Black background when in transition from PiP to fullscreen. The wallpaper gets into hidden state too early - App gets into PiP mode too early when entering PiP, need to defer the configuration change sent to app in this case Bug: 146594635 Bug: 148198539 Bug: 138144750 Bug: 149569903 Test: atest PinnedStackTests Test: atest PipAnimationControllerTest Test: atest RecentsAnimationTest Test: atest RecentTasksTest Test: atest com.android.server.wm.ActivityStarterTests Merged-In: Id0c8ce03fa26952daf5e3687b18b2eb2375b7d20 Change-Id: Id0c8ce03fa26952daf5e3687b18b2eb2375b7d20 --- api/test-current.txt | 1 - .../java/android/app/ActivityTaskManager.java | 19 - .../android/app/IActivityTaskManager.aidl | 37 -- core/java/android/app/ITaskStackListener.aidl | 10 - core/java/android/app/TaskStackListener.java | 10 - .../android/view/IPinnedStackController.aidl | 24 - .../android/view/IPinnedStackListener.aidl | 18 +- .../view/IRecentsAnimationController.aidl | 7 - .../IPinnedStackAnimationListener.aidl | 28 + .../shared/recents/ISystemUiProxy.aidl | 16 +- .../system/PinnedStackListenerForwarder.java | 27 +- .../RecentsAnimationControllerCompat.java | 9 - .../system/TaskStackChangeListener.java | 22 - .../system/TaskStackChangeListeners.java | 26 - .../android/systemui/pip/BasePipManager.java | 4 + .../systemui/pip/PipAnimationController.java | 317 +++++++++ .../systemui/pip/PipBoundsHandler.java | 68 +- .../systemui/pip/PipSnapAlgorithm.java | 33 - .../systemui/pip/PipTaskOrganizer.java | 291 ++++++++ .../src/com/android/systemui/pip/PipUI.java | 13 + .../systemui/pip/phone/PipManager.java | 109 +-- .../pip/phone/PipMenuActivityController.java | 17 +- .../systemui/pip/phone/PipMotionHelper.java | 98 +-- .../systemui/pip/phone/PipTouchHandler.java | 137 +--- .../android/systemui/pip/tv/PipManager.java | 65 +- .../recents/OverviewProxyService.java | 28 + .../pip/PipAnimationControllerTest.java | 181 +++++ .../systemui/pip/PipBoundsHandlerTest.java | 144 +--- .../am/ActivityManagerShellCommand.java | 21 - .../com/android/server/wm/ActivityRecord.java | 21 +- .../com/android/server/wm/ActivityStack.java | 349 +--------- .../server/wm/ActivityStackSupervisor.java | 34 - .../android/server/wm/ActivityStarter.java | 17 - .../server/wm/ActivityTaskManagerService.java | 116 +--- .../server/wm/BoundsAnimationController.java | 584 ---------------- .../server/wm/BoundsAnimationTarget.java | 78 --- .../com/android/server/wm/DisplayContent.java | 6 - .../server/wm/PinnedStackController.java | 99 +-- .../android/server/wm/RecentsAnimation.java | 7 - .../server/wm/RecentsAnimationController.java | 10 - .../server/wm/RootWindowContainer.java | 13 - .../core/java/com/android/server/wm/Task.java | 5 +- .../wm/TaskChangeNotificationController.java | 34 - .../server/wm/TaskOrganizerController.java | 3 +- .../com/android/server/wm/WindowState.java | 19 - .../server/wm/WindowStateAnimator.java | 10 +- .../server/wm/ActivityStarterTests.java | 35 - .../wm/BoundsAnimationControllerTests.java | 627 ------------------ .../android/server/wm/RecentTasksTest.java | 4 - .../wm/RecentsAnimationControllerTest.java | 2 +- .../server/wm/RecentsAnimationTest.java | 2 - 51 files changed, 1087 insertions(+), 2768 deletions(-) create mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/recents/IPinnedStackAnimationListener.aidl create mode 100644 packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java create mode 100644 packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java delete mode 100644 services/core/java/com/android/server/wm/BoundsAnimationController.java delete mode 100644 services/core/java/com/android/server/wm/BoundsAnimationTarget.java delete mode 100644 services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java diff --git a/api/test-current.txt b/api/test-current.txt index 2d15c0ea0ffb9..d32c77f4cd254 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 9ba56cf40161e..d48b35b6f0c87 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -361,25 +361,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. diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 5b61402314c4d..4e3aa7d8ebc0e 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -236,32 +236,8 @@ 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 37bdda0b03936..28b28dad5b828 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -45,16 +45,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. * diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index da0aadb3ea481..b892b8e51c887 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -56,16 +56,6 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { public void onPinnedActivityRestartAttempt(boolean clearedTask) throws RemoteException { } - @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) diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl index cb82f168d991e..1cf83a3f1e794 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 d01c9330dcb20..596d55aebc6db 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 @@ -54,12 +53,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 762366eb62955..530dffbf06209 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -76,13 +76,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 0000000000000..97aa512ea7df5 --- /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 b1d39f59f7891..80fd826f28c62 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 8c0ffb82f0edf..360244c9af52d 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); } } @@ -68,13 +66,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) { @@ -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 748f356c25b99..6cd6118ede6b6 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 5f92b28118072..1c6223b847de2 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 acce41cc942ee..cbdd3f8191f41 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 @@ -127,18 +127,6 @@ public class TaskStackChangeListeners extends TaskStackListener { .sendToTarget(); } - @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 { @@ -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 adee7f23e709e..38744fe1b670e 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 0000000000000..b5fd406de3683 --- /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 Type of property to animate, either alpha (float) or bounds (Rect) + */ + public abstract static class PipTransitionAnimator 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 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 ofAlpha(IWindowContainer wc, boolean scheduleFinishPip, + Rect destinationBounds, float startValue, float endValue) { + return new PipTransitionAnimator(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 ofBounds(IWindowContainer wc, boolean scheduleFinishPip, + Rect startValue, Rect endValue) { + // construct new Rect instances in case they are recycled + return new PipTransitionAnimator(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 41b31306a9313..8c3ccaab8249f 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; } @@ -154,14 +145,6 @@ public class PipBoundsHandler { mImeHeight = imeHeight; } - /** - * 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)); - } } /** @@ -435,20 +406,6 @@ public class PipBoundsHandler { return movementBounds; } - /** - * 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 f3e707c6408c4..6b89718acee82 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( @@ -91,13 +86,6 @@ public class PipSnapAlgorithm { calculateSnapTargets(); } - /** - * 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 @@ -234,20 +222,6 @@ public class PipSnapAlgorithm { return newBounds; } - /** - * 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 @@ -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 0000000000000..1555153e8d4fe --- /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 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 599c845ba39a1..4fb675e31f0e1 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 cb94e28e3467f..e98dec0835bfd 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. @@ -123,20 +126,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 c7bfc06829b3d..d660b670446bb 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; @@ -99,11 +95,6 @@ public class PipMenuActivityController { */ void onPipExpand(); - /** - * Called when the PIP requested to be minimized. - */ - void onPipMinimize(); - /** * Called when the PIP requested to be dismissed. */ @@ -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 c6e28522ccbd6..91f539c3a13d9 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; @@ -284,35 +283,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return mBounds; } - /** - * @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. */ @@ -327,25 +297,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return false; } - /** - * 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. */ @@ -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(); } @@ -593,13 +540,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call return dismissArea.contains(endpoint.x, endpoint.y); } - /** - * @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. */ @@ -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 8e588e67861c1..79a25b2269f6b 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; } @@ -481,44 +464,6 @@ public class PipTouchHandler { mPinnedStackController = controller; } - /** - * 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. */ @@ -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 487c2533b0bb6..cb1a218af954f 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); } /** @@ -448,13 +447,6 @@ public class PipManager implements BasePipManager { return mState; } - /** - * 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,19 +684,28 @@ 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()"); - - switch (getState()) { - case STATE_PIP_MENU: - showPipMenu(); - break; - } - } }; + @Override + public void onPipTransitionStarted() { } + + @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 34cad51e1c9fc..1cd63881a7005 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 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 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 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 destinationBounds = ArgumentCaptor.forClass(Rect.class); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); + oldPosition.offset(0, -100); + mPipBoundsHandler.onSaveReentryBounds(componentName, oldPosition); - 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); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds( + DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS); - mPipBoundsHandler.onPrepareAnimation(null, 1f, null); - - 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 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 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 c7f5f630ca1a6..bf797291380a2 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 [true|false]"); pw.println(" Move from its current stack to the top (true) or"); pw.println(" bottom (false) of ."); - pw.println(" resize-animated "); - pw.println(" Same as resize, but allow animation."); pw.println(" resize-docked-stack []"); pw.println(" Change docked stack to "); 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 190af7a06c8dc..d715ed408d51f 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 2f1cc0532ccc9..ff890ff13ee3e 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 */); } @@ -3703,34 +3588,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. */ @@ -3762,30 +3619,6 @@ class ActivityStack extends Task implements BoundsAnimationTarget { getBounds(out); } - /** - * 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 @@ -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 97b638820a8af..b2d2f624e458e 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 600a125cdf43a..a467e736abcc0 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 5392257ae0b03..945893986ae3b 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 5385e2fce0084..0000000000000 --- 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 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 b1d535904fab4..0000000000000 --- 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 0029dc8c3c25d..8ccb59f39081a 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 mClosingApps = new ArraySet<>(); final ArraySet mChangingApps = new ArraySet<>(); final UnknownAppVisibilityController mUnknownAppVisibilityController; - BoundsAnimationController mBoundsAnimationController; private MetricsLogger mMetricsLogger; @@ -977,10 +975,6 @@ class DisplayContent extends WindowContainer { - 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. */ @@ -330,19 +274,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. */ @@ -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 e92bbaae71d38..adafdec40d37f 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 6b6b946818798..4399d65193637 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); } @@ -308,14 +306,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) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index aa6bdfd1aa5be..64d7db26cd7e8 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 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 ecfac1bc7b6ef..76805e9f6342d 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 { * 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 f715d8f3529b0..96a9127344e8d 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 6caa27c7fa6f1..0f1e623a38f1e 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 b25008373a7bd..37597fbc7c457 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 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 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 a1a9af680e92c..81d0e3e88c1a8 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 b917e1b92fb91..4fe6d7892cfeb 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; @@ -138,39 +136,6 @@ public class ActivityStarterTests extends ActivityTestsBase { mock(ActivityStartInterceptor.class)); } - @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); 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 1dda535cfb951..0000000000000 --- 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 444906977f47e..d9c5c4c7dffcf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1101,13 +1101,9 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0)); 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 c9efb7018616b..5c36f5c39e0c6 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 b78107e9024f4..b3a253029ed00 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)