blob: 00f693de8f4db237c0fc4ef9c27ef5b9c7726b23 [file] [log] [blame]
Winson Chung2a82fe52017-02-02 14:43:34 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.pip.phone;
18
Wale Ogunwale68278562017-09-23 17:13:55 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Gus Prevasab336792018-11-14 13:52:20 -050020
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050021import android.annotation.NonNull;
Joshua Tsuji2e252462020-01-02 11:27:41 -050022import android.annotation.Nullable;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070023import android.app.IActivityTaskManager;
Winson Chung2a82fe52017-02-02 14:43:34 -080024import android.content.Context;
Winson Chung2a82fe52017-02-02 14:43:34 -080025import android.graphics.Rect;
Winson Chungbb233762017-05-15 14:20:46 -070026import android.os.Debug;
Winson Chung2a82fe52017-02-02 14:43:34 -080027import android.os.RemoteException;
28import android.util.Log;
Joshua Tsuji2e252462020-01-02 11:27:41 -050029
30import androidx.dynamicanimation.animation.SpringForce;
Gus Prevasab336792018-11-14 13:52:20 -050031
Hongwei Wangebf18082019-09-26 14:25:11 -070032import com.android.systemui.pip.PipSnapAlgorithm;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080033import com.android.systemui.pip.PipTaskOrganizer;
Winson Chung67f5c8b2018-09-24 12:09:19 -070034import com.android.systemui.shared.system.WindowManagerWrapper;
Winson Chung2a82fe52017-02-02 14:43:34 -080035import com.android.systemui.statusbar.FlingAnimationUtils;
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050036import com.android.systemui.util.FloatingContentCoordinator;
Joshua Tsuji2e252462020-01-02 11:27:41 -050037import com.android.systemui.util.animation.FloatProperties;
38import com.android.systemui.util.animation.PhysicsAnimator;
Joshua Tsujif39539d2020-04-03 18:53:06 -040039import com.android.systemui.util.magnetictarget.MagnetizedObject;
Gus Prevasab336792018-11-14 13:52:20 -050040
Winson Chung29a78652017-02-09 18:35:26 -080041import java.io.PrintWriter;
Winson Chung55701472020-03-04 19:30:30 -080042import java.util.function.Consumer;
Winson Chung29a78652017-02-09 18:35:26 -080043
Winson Chung2a82fe52017-02-02 14:43:34 -080044/**
45 * A helper to animate and manipulate the PiP.
46 */
Winson Chung55701472020-03-04 19:30:30 -080047public class PipMotionHelper implements PipAppOpsListener.Callback,
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050048 FloatingContentCoordinator.FloatingContent {
Winson Chung2a82fe52017-02-02 14:43:34 -080049
50 private static final String TAG = "PipMotionHelper";
Winson Chungbb233762017-05-15 14:20:46 -070051 private static final boolean DEBUG = false;
Winson Chung2a82fe52017-02-02 14:43:34 -080052
Winson Chungbaa7b722017-03-03 21:33:44 -080053 private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
54 private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
55 private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
Tracy Zhou43513082018-03-08 21:58:36 -080056 private static final int SHIFT_DURATION = 300;
Winson Chung2a82fe52017-02-02 14:43:34 -080057
Joshua Tsuji2e252462020-01-02 11:27:41 -050058 /** Friction to use for PIP when it moves via physics fling animations. */
59 private static final float DEFAULT_FRICTION = 2f;
60
Hongwei Wang85cf41f2020-01-15 15:14:47 -080061 private final Context mContext;
62 private final IActivityTaskManager mActivityTaskManager;
63 private final PipTaskOrganizer mPipTaskOrganizer;
Winson Chung2a82fe52017-02-02 14:43:34 -080064
Winson Chung79f852e2017-05-04 15:06:18 -070065 private PipMenuActivityController mMenuController;
Winson Chung2a82fe52017-02-02 14:43:34 -080066 private PipSnapAlgorithm mSnapAlgorithm;
67 private FlingAnimationUtils mFlingAnimationUtils;
68
Winson Chung2a82fe52017-02-02 14:43:34 -080069 private final Rect mStableInsets = new Rect();
70
Joshua Tsuji2e252462020-01-02 11:27:41 -050071 /** PIP's current bounds on the screen. */
72 private final Rect mBounds = new Rect();
73
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050074 /** The bounds within which PIP's top-left coordinate is allowed to move. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070075 private final Rect mMovementBounds = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050076
77 /** The region that all of PIP must stay within. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070078 private final Rect mFloatingAllowedArea = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050079
Joshua Tsuji2e252462020-01-02 11:27:41 -050080 /**
Joshua Tsuji05844702020-02-04 18:24:35 -080081 * Bounds that are animated using the physics animator.
Joshua Tsuji2e252462020-01-02 11:27:41 -050082 */
83 private final Rect mAnimatedBounds = new Rect();
84
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050085 /** The destination bounds to which PIP is animating. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070086 private final Rect mAnimatingToBounds = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050087
88 /** Coordinator instance for resolving conflicts with other floating content. */
89 private FloatingContentCoordinator mFloatingContentCoordinator;
90
Joshua Tsuji2e252462020-01-02 11:27:41 -050091 /**
92 * PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
93 */
94 private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
95 mAnimatedBounds);
96
97 /**
Winson Chung55701472020-03-04 19:30:30 -080098 * Update listener that resizes the PIP to {@link #mAnimatedBounds}.
Joshua Tsuji2e252462020-01-02 11:27:41 -050099 */
Joshua Tsujif39539d2020-04-03 18:53:06 -0400100 final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
Winson Chung55701472020-03-04 19:30:30 -0800101 (target, values) -> resizePipUnchecked(mAnimatedBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500102
103 /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
104 private PhysicsAnimator.FlingConfig mFlingConfigX;
105 private PhysicsAnimator.FlingConfig mFlingConfigY;
106
107 /** SpringConfig to use for fling-then-spring animations. */
108 private final PhysicsAnimator.SpringConfig mSpringConfig =
109 new PhysicsAnimator.SpringConfig(
110 SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
Winson Chung2a82fe52017-02-02 14:43:34 -0800111
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500112 /** SpringConfig to use for springing PIP away from conflicting floating content. */
113 private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
114 new PhysicsAnimator.SpringConfig(
115 SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
116
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700117 private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
Winson Chung55701472020-03-04 19:30:30 -0800118
Joshua Tsujif39539d2020-04-03 18:53:06 -0400119 /**
120 * Whether we're springing to the touch event location (vs. moving it to that position
121 * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was
122 * 'stuck' in the target and needs to catch up to the touch location.
123 */
124 private boolean mSpringingToTouch = false;
125
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800126 public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
127 PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500128 PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
129 FloatingContentCoordinator floatingContentCoordinator) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800130 mContext = context;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700131 mActivityTaskManager = activityTaskManager;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800132 mPipTaskOrganizer = pipTaskOrganizer;
Winson Chung79f852e2017-05-04 15:06:18 -0700133 mMenuController = menuController;
Winson Chung2a82fe52017-02-02 14:43:34 -0800134 mSnapAlgorithm = snapAlgorithm;
135 mFlingAnimationUtils = flingAnimationUtils;
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500136 mFloatingContentCoordinator = floatingContentCoordinator;
Winson Chung2a82fe52017-02-02 14:43:34 -0800137 onConfigurationChanged();
138 }
139
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500140 @NonNull
141 @Override
142 public Rect getFloatingBoundsOnScreen() {
143 return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds;
144 }
145
146 @NonNull
147 @Override
148 public Rect getAllowedFloatingBoundsRegion() {
149 return mFloatingAllowedArea;
150 }
151
152 @Override
153 public void moveToBounds(@NonNull Rect bounds) {
154 animateToBounds(bounds, mConflictResolutionSpringConfig);
155 }
156
Winson Chung2a82fe52017-02-02 14:43:34 -0800157 /**
158 * Updates whenever the configuration changes.
159 */
160 void onConfigurationChanged() {
161 mSnapAlgorithm.onConfigurationChanged();
Winson Chung67f5c8b2018-09-24 12:09:19 -0700162 WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
Winson Chung2a82fe52017-02-02 14:43:34 -0800163 }
164
165 /**
Joshua Tsuji601dc852020-04-02 15:54:15 -0400166 * Synchronizes the current bounds with the pinned stack, cancelling any ongoing animations.
Winson Chung2a82fe52017-02-02 14:43:34 -0800167 */
168 void synchronizePinnedStackBounds() {
169 cancelAnimations();
Ben Lin3cd7bc32020-04-07 17:15:21 -0700170 mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
Joshua Tsujifaf890f2020-04-16 13:38:32 -0400171 mFloatingContentCoordinator.onContentMoved(this);
Winson Chung2a82fe52017-02-02 14:43:34 -0800172 }
173
174 /**
Joshua Tsuji601dc852020-04-02 15:54:15 -0400175 * Synchronizes the current bounds with either the pinned stack, or the ongoing animation. This
176 * is done to prepare for a touch gesture.
177 */
178 void synchronizePinnedStackBoundsForTouchGesture() {
179 if (mAnimatingToBounds.isEmpty()) {
180 // If we're not animating anywhere, sync normally.
181 synchronizePinnedStackBounds();
182 } else {
183 // If we're animating, set the current bounds to the animated bounds. That way, the
184 // touch gesture will begin at the most recent animated location of the bounds.
185 mBounds.set(mAnimatedBounds);
186 }
187 }
188
189 /**
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500190 * Tries to move the pinned stack to the given {@param bounds}.
Winson Chung2a82fe52017-02-02 14:43:34 -0800191 */
192 void movePip(Rect toBounds) {
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500193 movePip(toBounds, false /* isDragging */);
194 }
195
196 /**
197 * Tries to move the pinned stack to the given {@param bounds}.
198 *
199 * @param isDragging Whether this movement is the result of a drag touch gesture. If so, we
200 * won't notify the floating content coordinator of this move, since that will
201 * happen when the gesture ends.
202 */
203 void movePip(Rect toBounds, boolean isDragging) {
204 if (!isDragging) {
205 mFloatingContentCoordinator.onContentMoved(this);
206 }
207
Joshua Tsujif39539d2020-04-03 18:53:06 -0400208 if (!mSpringingToTouch) {
209 // If we are moving PIP directly to the touch event locations, cancel any animations and
210 // move PIP to the given bounds.
211 cancelAnimations();
212 resizePipUnchecked(toBounds);
213 mBounds.set(toBounds);
214 } else {
215 // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
216 // to spring towards the new touch location.
217 mAnimatedBoundsPhysicsAnimator
218 .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
219 .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
220 .withEndActions(() -> mSpringingToTouch = false);
221
222 startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */);
223 }
224 }
225
226 /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
227 void setSpringingToTouch(boolean springingToTouch) {
228 if (springingToTouch) {
229 mAnimatedBounds.set(mBounds);
230 }
231
232 mSpringingToTouch = springingToTouch;
233 }
234
235 void prepareForAnimation() {
236 mAnimatedBounds.set(mBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800237 }
238
239 /**
240 * Resizes the pinned stack back to fullscreen.
241 */
242 void expandPip() {
Winson Chunge6385a22017-05-02 18:15:16 -0700243 expandPip(false /* skipAnimation */);
244 }
245
246 /**
247 * Resizes the pinned stack back to fullscreen.
248 */
249 void expandPip(boolean skipAnimation) {
Winson Chungbb233762017-05-15 14:20:46 -0700250 if (DEBUG) {
251 Log.d(TAG, "expandPip: skipAnimation=" + skipAnimation
252 + " callers=\n" + Debug.getCallers(5, " "));
253 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800254 cancelAnimations();
Winson Chung79f852e2017-05-04 15:06:18 -0700255 mMenuController.hideMenuWithoutResize();
Winson Chung55701472020-03-04 19:30:30 -0800256 mPipTaskOrganizer.getUpdateHandler().post(() -> {
Hongwei Wangd39583a2020-03-04 11:14:32 -0800257 mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION);
Winson Chung2a82fe52017-02-02 14:43:34 -0800258 });
259 }
260
261 /**
262 * Dismisses the pinned stack.
263 */
Eliot Courtney293d86e2019-01-08 16:42:39 +0900264 @Override
265 public void dismissPip() {
Winson Chungbb233762017-05-15 14:20:46 -0700266 if (DEBUG) {
267 Log.d(TAG, "dismissPip: callers=\n" + Debug.getCallers(5, " "));
268 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800269 cancelAnimations();
Winson Chung79f852e2017-05-04 15:06:18 -0700270 mMenuController.hideMenuWithoutResize();
Winson Chung55701472020-03-04 19:30:30 -0800271 mPipTaskOrganizer.getUpdateHandler().post(() -> {
Winson Chung2a82fe52017-02-02 14:43:34 -0800272 try {
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700273 mActivityTaskManager.removeStacksInWindowingModes(
274 new int[]{ WINDOWING_MODE_PINNED });
Winson Chung2a82fe52017-02-02 14:43:34 -0800275 } catch (RemoteException e) {
276 Log.e(TAG, "Failed to remove PiP", e);
277 }
278 });
279 }
280
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500281 /** Sets the movement bounds to use to constrain PIP position animations. */
282 void setCurrentMovementBounds(Rect movementBounds) {
283 mMovementBounds.set(movementBounds);
284 rebuildFlingConfigs();
285
286 // The movement bounds represent the area within which we can move PIP's top-left position.
287 // The allowed area for all of PIP is those bounds plus PIP's width and height.
288 mFloatingAllowedArea.set(mMovementBounds);
289 mFloatingAllowedArea.right += mBounds.width();
290 mFloatingAllowedArea.bottom += mBounds.height();
291 }
292
Winson Chung2a82fe52017-02-02 14:43:34 -0800293 /**
294 * @return the PiP bounds.
295 */
296 Rect getBounds() {
297 return mBounds;
298 }
299
300 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800301 * Flings the PiP to the closest snap target.
302 */
Joshua Tsuji2e252462020-01-02 11:27:41 -0500303 void flingToSnapTarget(
Joshua Tsujif39539d2020-04-03 18:53:06 -0400304 float velocityX, float velocityY,
305 @Nullable Runnable updateAction, @Nullable Runnable endAction) {
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500306 mAnimatedBounds.set(mBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500307 mAnimatedBoundsPhysicsAnimator
308 .flingThenSpring(
309 FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
310 true /* flingMustReachMinOrMax */)
311 .flingThenSpring(
312 FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500313 .withEndActions(endAction);
314
Joshua Tsujif39539d2020-04-03 18:53:06 -0400315 if (updateAction != null) {
316 mAnimatedBoundsPhysicsAnimator.addUpdateListener(
317 (target, values) -> updateAction.run());
318 }
319
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500320 final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
321 final float estimatedFlingYEndValue =
322 PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
323
Joshua Tsuji601dc852020-04-02 15:54:15 -0400324 startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800325 }
326
327 /**
328 * Animates the PiP to the closest snap target.
329 */
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500330 void animateToClosestSnapTarget() {
Hongwei Wangdb779c32020-03-02 17:20:57 -0800331 final Rect newBounds = new Rect();
332 mSnapAlgorithm.snapRectToClosestEdge(mBounds, mMovementBounds, newBounds);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500333 animateToBounds(newBounds, mSpringConfig);
334 }
Joshua Tsuji2e252462020-01-02 11:27:41 -0500335
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500336 /**
337 * Animates PIP to the provided bounds, using physics animations and the given spring
338 * configuration
339 */
340 void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
341 mAnimatedBounds.set(mBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500342 mAnimatedBoundsPhysicsAnimator
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500343 .spring(FloatProperties.RECT_X, bounds.left, springConfig)
344 .spring(FloatProperties.RECT_Y, bounds.top, springConfig);
Joshua Tsuji601dc852020-04-02 15:54:15 -0400345 startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500346 }
347
348 /**
349 * Animates the dismissal of the PiP off the edge of the screen.
350 */
351 void animateDismiss(float velocityX, float velocityY, @Nullable Runnable updateAction) {
Joshua Tsuji2e252462020-01-02 11:27:41 -0500352 mAnimatedBounds.set(mBounds);
353
Joshua Tsujif39539d2020-04-03 18:53:06 -0400354 // Animate off the bottom of the screen, then dismiss PIP.
Joshua Tsuji2e252462020-01-02 11:27:41 -0500355 mAnimatedBoundsPhysicsAnimator
Joshua Tsujif39539d2020-04-03 18:53:06 -0400356 .spring(FloatProperties.RECT_Y,
357 mBounds.bottom + mBounds.height(),
358 velocityY,
359 mSpringConfig)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500360 .withEndActions(this::dismissPip);
361
362 // If we were provided with an update action, run it whenever there's an update.
363 if (updateAction != null) {
364 mAnimatedBoundsPhysicsAnimator.addUpdateListener(
365 (target, values) -> updateAction.run());
Winson Chung2a82fe52017-02-02 14:43:34 -0800366 }
Joshua Tsuji2e252462020-01-02 11:27:41 -0500367
Joshua Tsujif39539d2020-04-03 18:53:06 -0400368 startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800369 }
370
371 /**
372 * Animates the PiP to the expanded state to show the menu.
373 */
374 float animateToExpandedState(Rect expandedBounds, Rect movementBounds,
375 Rect expandedMovementBounds) {
376 float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
377 mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
Winson Chungd2d90972017-02-28 11:40:41 -0800378 resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
Winson Chung2a82fe52017-02-02 14:43:34 -0800379 return savedSnapFraction;
380 }
381
382 /**
383 * Animates the PiP from the expanded state to the normal state after the menu is hidden.
384 */
385 void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800386 Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
Winson Chungd2d90972017-02-28 11:40:41 -0800387 if (savedSnapFraction < 0f) {
388 // If there are no saved snap fractions, then just use the current bounds
389 savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
390 currentMovementBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800391 }
Winson Chungd2d90972017-02-28 11:40:41 -0800392 mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500393
Winson Chungef4dc812017-04-11 13:31:44 -0700394 if (immediate) {
395 movePip(normalBounds);
396 } else {
397 resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
398 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800399 }
400
401 /**
Tracy Zhou43513082018-03-08 21:58:36 -0800402 * Animates the PiP to offset it from the IME or shelf.
Winson Chungbaa7b722017-03-03 21:33:44 -0800403 */
wilsonshih5c4cf522019-01-25 09:03:47 +0800404 void animateToOffset(Rect originalBounds, int offset) {
Winson Chung55701472020-03-04 19:30:30 -0800405 if (DEBUG) {
406 Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
407 + " callers=\n" + Debug.getCallers(5, " "));
408 }
Winson Chungbaa7b722017-03-03 21:33:44 -0800409 cancelAnimations();
Winson Chung55701472020-03-04 19:30:30 -0800410 mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
411 mUpdateBoundsCallback);
Winson Chungbaa7b722017-03-03 21:33:44 -0800412 }
413
414 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800415 * Cancels all existing animations.
416 */
Joshua Tsuji2e252462020-01-02 11:27:41 -0500417 private void cancelAnimations() {
418 mAnimatedBoundsPhysicsAnimator.cancel();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500419 mAnimatingToBounds.setEmpty();
Joshua Tsujif39539d2020-04-03 18:53:06 -0400420 mSpringingToTouch = false;
Winson Chung2a82fe52017-02-02 14:43:34 -0800421 }
422
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500423 /** Set new fling configs whose min/max values respect the given movement bounds. */
424 private void rebuildFlingConfigs() {
Joshua Tsuji2e252462020-01-02 11:27:41 -0500425 mFlingConfigX = new PhysicsAnimator.FlingConfig(
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500426 DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500427 mFlingConfigY = new PhysicsAnimator.FlingConfig(
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500428 DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500429 }
430
431 /**
432 * Starts the physics animator which will update the animated PIP bounds using physics
Winson Chung55701472020-03-04 19:30:30 -0800433 * animations, as well as the TimeAnimator which will apply those bounds to PIP.
Joshua Tsuji2e252462020-01-02 11:27:41 -0500434 *
435 * This will also add end actions to the bounds animator that cancel the TimeAnimator and update
436 * the 'real' bounds to equal the final animated bounds.
437 */
Joshua Tsuji601dc852020-04-02 15:54:15 -0400438 private void startBoundsAnimator(float toX, float toY) {
Joshua Tsujif39539d2020-04-03 18:53:06 -0400439 if (!mSpringingToTouch) {
440 cancelAnimations();
441 }
Joshua Tsuji2e252462020-01-02 11:27:41 -0500442
Joshua Tsuji601dc852020-04-02 15:54:15 -0400443 // Set animatingToBounds directly to avoid allocating a new Rect, but then call
444 // setAnimatingToBounds to run the normal logic for changing animatingToBounds.
445 mAnimatingToBounds.set(
446 (int) toX,
447 (int) toY,
448 (int) toX + mBounds.width(),
449 (int) toY + mBounds.height());
450 setAnimatingToBounds(mAnimatingToBounds);
451
Joshua Tsuji2e252462020-01-02 11:27:41 -0500452 mAnimatedBoundsPhysicsAnimator
Joshua Tsuji601dc852020-04-02 15:54:15 -0400453 .withEndActions(() -> {
454 mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds);
455 mAnimatingToBounds.setEmpty();
456 })
Winson Chung55701472020-03-04 19:30:30 -0800457 .addUpdateListener(mResizePipUpdateListener)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500458 .start();
Winson Chung2a82fe52017-02-02 14:43:34 -0800459 }
460
461 /**
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500462 * Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
463 * we return these bounds from
464 * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
465 */
466 private void setAnimatingToBounds(Rect bounds) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700467 mAnimatingToBounds.set(bounds);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500468 mFloatingContentCoordinator.onContentMoved(this);
469 }
470
471 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800472 * Directly resizes the PiP to the given {@param bounds}.
473 */
474 private void resizePipUnchecked(Rect toBounds) {
Winson Chungbb233762017-05-15 14:20:46 -0700475 if (DEBUG) {
476 Log.d(TAG, "resizePipUnchecked: toBounds=" + toBounds
477 + " callers=\n" + Debug.getCallers(5, " "));
478 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800479 if (!toBounds.equals(mBounds)) {
Winson Chung55701472020-03-04 19:30:30 -0800480 mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
Winson Chungd2d90972017-02-28 11:40:41 -0800481 }
482 }
483
484 /**
485 * Directly resizes the PiP to the given {@param bounds}.
486 */
487 private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
Winson Chungbb233762017-05-15 14:20:46 -0700488 if (DEBUG) {
489 Log.d(TAG, "resizeAndAnimatePipUnchecked: toBounds=" + toBounds
490 + " duration=" + duration + " callers=\n" + Debug.getCallers(5, " "));
491 }
Winson Chungd2d90972017-02-28 11:40:41 -0800492 if (!toBounds.equals(mBounds)) {
Winson Chung55701472020-03-04 19:30:30 -0800493 mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500494 setAnimatingToBounds(toBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800495 }
496 }
497
498 /**
Joshua Tsujif39539d2020-04-03 18:53:06 -0400499 * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
500 * magnetic dismiss target so it can calculate PIP's size and position.
Mady Mellor60421c92017-03-29 15:27:37 -0700501 */
Joshua Tsujif39539d2020-04-03 18:53:06 -0400502 MagnetizedObject<Rect> getMagnetizedPip() {
503 return new MagnetizedObject<Rect>(
504 mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
505 @Override
506 public float getWidth(@NonNull Rect animatedPipBounds) {
507 return animatedPipBounds.width();
508 }
Mady Mellor60421c92017-03-29 15:27:37 -0700509
Joshua Tsujif39539d2020-04-03 18:53:06 -0400510 @Override
511 public float getHeight(@NonNull Rect animatedPipBounds) {
512 return animatedPipBounds.height();
513 }
Mady Mellor47ba1402017-04-04 17:25:43 -0700514
Joshua Tsujif39539d2020-04-03 18:53:06 -0400515 @Override
516 public void getLocationOnScreen(
517 @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
518 loc[0] = animatedPipBounds.left;
519 loc[1] = animatedPipBounds.top;
520 }
521 };
Mady Mellor47ba1402017-04-04 17:25:43 -0700522 }
523
Winson Chung29a78652017-02-09 18:35:26 -0800524 public void dump(PrintWriter pw, String prefix) {
525 final String innerPrefix = prefix + " ";
526 pw.println(prefix + TAG);
527 pw.println(innerPrefix + "mBounds=" + mBounds);
528 pw.println(innerPrefix + "mStableInsets=" + mStableInsets);
529 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800530}