blob: 08d42224c3c06eea628b278eb73fe88701735428 [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;
Hongwei Wang7c622402020-05-07 20:45:22 -070024import android.content.ComponentName;
Winson Chung2a82fe52017-02-02 14:43:34 -080025import android.content.Context;
Winson Chung2a82fe52017-02-02 14:43:34 -080026import android.graphics.Rect;
Winson Chungbb233762017-05-15 14:20:46 -070027import android.os.Debug;
Winson Chung2a82fe52017-02-02 14:43:34 -080028import android.os.RemoteException;
29import android.util.Log;
Joshua Tsuji2e252462020-01-02 11:27:41 -050030
31import androidx.dynamicanimation.animation.SpringForce;
Gus Prevasab336792018-11-14 13:52:20 -050032
Hongwei Wangebf18082019-09-26 14:25:11 -070033import com.android.systemui.pip.PipSnapAlgorithm;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080034import com.android.systemui.pip.PipTaskOrganizer;
Winson Chung67f5c8b2018-09-24 12:09:19 -070035import com.android.systemui.shared.system.WindowManagerWrapper;
Winson Chung2a82fe52017-02-02 14:43:34 -080036import com.android.systemui.statusbar.FlingAnimationUtils;
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050037import com.android.systemui.util.FloatingContentCoordinator;
Joshua Tsuji2e252462020-01-02 11:27:41 -050038import com.android.systemui.util.animation.FloatProperties;
39import com.android.systemui.util.animation.PhysicsAnimator;
Joshua Tsujif39539d2020-04-03 18:53:06 -040040import com.android.systemui.util.magnetictarget.MagnetizedObject;
Gus Prevasab336792018-11-14 13:52:20 -050041
Winson Chung29a78652017-02-09 18:35:26 -080042import java.io.PrintWriter;
Winson Chung55701472020-03-04 19:30:30 -080043import java.util.function.Consumer;
Winson Chung29a78652017-02-09 18:35:26 -080044
Winson Chung2a82fe52017-02-02 14:43:34 -080045/**
46 * A helper to animate and manipulate the PiP.
47 */
Winson Chung55701472020-03-04 19:30:30 -080048public class PipMotionHelper implements PipAppOpsListener.Callback,
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050049 FloatingContentCoordinator.FloatingContent {
Winson Chung2a82fe52017-02-02 14:43:34 -080050
51 private static final String TAG = "PipMotionHelper";
Winson Chungbb233762017-05-15 14:20:46 -070052 private static final boolean DEBUG = false;
Winson Chung2a82fe52017-02-02 14:43:34 -080053
Winson Chungbaa7b722017-03-03 21:33:44 -080054 private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
55 private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
56 private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
Tracy Zhou43513082018-03-08 21:58:36 -080057 private static final int SHIFT_DURATION = 300;
Winson Chung2a82fe52017-02-02 14:43:34 -080058
Joshua Tsuji2e252462020-01-02 11:27:41 -050059 /** Friction to use for PIP when it moves via physics fling animations. */
60 private static final float DEFAULT_FRICTION = 2f;
61
Hongwei Wang85cf41f2020-01-15 15:14:47 -080062 private final Context mContext;
63 private final IActivityTaskManager mActivityTaskManager;
64 private final PipTaskOrganizer mPipTaskOrganizer;
Winson Chung2a82fe52017-02-02 14:43:34 -080065
Winson Chung79f852e2017-05-04 15:06:18 -070066 private PipMenuActivityController mMenuController;
Winson Chung2a82fe52017-02-02 14:43:34 -080067 private PipSnapAlgorithm mSnapAlgorithm;
68 private FlingAnimationUtils mFlingAnimationUtils;
69
Winson Chung2a82fe52017-02-02 14:43:34 -080070 private final Rect mStableInsets = new Rect();
71
Joshua Tsuji2e252462020-01-02 11:27:41 -050072 /** PIP's current bounds on the screen. */
73 private final Rect mBounds = new Rect();
74
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050075 /** The bounds within which PIP's top-left coordinate is allowed to move. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070076 private final Rect mMovementBounds = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050077
78 /** The region that all of PIP must stay within. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070079 private final Rect mFloatingAllowedArea = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050080
Joshua Tsuji2e252462020-01-02 11:27:41 -050081 /**
Joshua Tsuji05844702020-02-04 18:24:35 -080082 * Bounds that are animated using the physics animator.
Joshua Tsuji2e252462020-01-02 11:27:41 -050083 */
84 private final Rect mAnimatedBounds = new Rect();
85
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050086 /** The destination bounds to which PIP is animating. */
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070087 private final Rect mAnimatingToBounds = new Rect();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -050088
89 /** Coordinator instance for resolving conflicts with other floating content. */
90 private FloatingContentCoordinator mFloatingContentCoordinator;
91
Joshua Tsuji2e252462020-01-02 11:27:41 -050092 /**
93 * PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
94 */
95 private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
96 mAnimatedBounds);
97
98 /**
Winson Chung55701472020-03-04 19:30:30 -080099 * Update listener that resizes the PIP to {@link #mAnimatedBounds}.
Joshua Tsuji2e252462020-01-02 11:27:41 -0500100 */
Joshua Tsujif39539d2020-04-03 18:53:06 -0400101 final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
Winson Chung55701472020-03-04 19:30:30 -0800102 (target, values) -> resizePipUnchecked(mAnimatedBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500103
104 /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
105 private PhysicsAnimator.FlingConfig mFlingConfigX;
106 private PhysicsAnimator.FlingConfig mFlingConfigY;
107
108 /** SpringConfig to use for fling-then-spring animations. */
109 private final PhysicsAnimator.SpringConfig mSpringConfig =
110 new PhysicsAnimator.SpringConfig(
111 SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
Winson Chung2a82fe52017-02-02 14:43:34 -0800112
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500113 /** SpringConfig to use for springing PIP away from conflicting floating content. */
114 private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
115 new PhysicsAnimator.SpringConfig(
116 SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
117
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700118 private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set;
Winson Chung55701472020-03-04 19:30:30 -0800119
Joshua Tsujif39539d2020-04-03 18:53:06 -0400120 /**
121 * Whether we're springing to the touch event location (vs. moving it to that position
122 * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was
123 * 'stuck' in the target and needs to catch up to the touch location.
124 */
125 private boolean mSpringingToTouch = false;
126
Hongwei Wang7c622402020-05-07 20:45:22 -0700127 /**
128 * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
129 * used to show menu activity when the expand animation is completed.
130 */
131 private Runnable mPostPipTransitionCallback;
132
133 private final PipTaskOrganizer.PipTransitionCallback mPipTransitionCallback =
134 new PipTaskOrganizer.PipTransitionCallback() {
135 @Override
136 public void onPipTransitionStarted(ComponentName activity, int direction) {}
137
138 @Override
139 public void onPipTransitionFinished(ComponentName activity, int direction) {
140 if (mPostPipTransitionCallback != null) {
141 mPostPipTransitionCallback.run();
142 mPostPipTransitionCallback = null;
143 }
144 }
145
146 @Override
147 public void onPipTransitionCanceled(ComponentName activity, int direction) {}
148 };
149
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800150 public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
151 PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500152 PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
153 FloatingContentCoordinator floatingContentCoordinator) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800154 mContext = context;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700155 mActivityTaskManager = activityTaskManager;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800156 mPipTaskOrganizer = pipTaskOrganizer;
Winson Chung79f852e2017-05-04 15:06:18 -0700157 mMenuController = menuController;
Winson Chung2a82fe52017-02-02 14:43:34 -0800158 mSnapAlgorithm = snapAlgorithm;
159 mFlingAnimationUtils = flingAnimationUtils;
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500160 mFloatingContentCoordinator = floatingContentCoordinator;
Winson Chung2a82fe52017-02-02 14:43:34 -0800161 onConfigurationChanged();
Hongwei Wang7c622402020-05-07 20:45:22 -0700162 mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
Winson Chung2a82fe52017-02-02 14:43:34 -0800163 }
164
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500165 @NonNull
166 @Override
167 public Rect getFloatingBoundsOnScreen() {
168 return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds;
169 }
170
171 @NonNull
172 @Override
173 public Rect getAllowedFloatingBoundsRegion() {
174 return mFloatingAllowedArea;
175 }
176
177 @Override
178 public void moveToBounds(@NonNull Rect bounds) {
179 animateToBounds(bounds, mConflictResolutionSpringConfig);
180 }
181
Winson Chung2a82fe52017-02-02 14:43:34 -0800182 /**
183 * Updates whenever the configuration changes.
184 */
185 void onConfigurationChanged() {
186 mSnapAlgorithm.onConfigurationChanged();
Winson Chung67f5c8b2018-09-24 12:09:19 -0700187 WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
Winson Chung2a82fe52017-02-02 14:43:34 -0800188 }
189
190 /**
Joshua Tsuji601dc852020-04-02 15:54:15 -0400191 * Synchronizes the current bounds with the pinned stack, cancelling any ongoing animations.
Winson Chung2a82fe52017-02-02 14:43:34 -0800192 */
193 void synchronizePinnedStackBounds() {
194 cancelAnimations();
Ben Lin3cd7bc32020-04-07 17:15:21 -0700195 mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
Joshua Tsuji0d4cbeb2020-05-01 12:45:41 -0400196
197 if (mPipTaskOrganizer.isInPip()) {
198 mFloatingContentCoordinator.onContentMoved(this);
199 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800200 }
201
202 /**
Joshua Tsuji601dc852020-04-02 15:54:15 -0400203 * Synchronizes the current bounds with either the pinned stack, or the ongoing animation. This
204 * is done to prepare for a touch gesture.
205 */
206 void synchronizePinnedStackBoundsForTouchGesture() {
207 if (mAnimatingToBounds.isEmpty()) {
208 // If we're not animating anywhere, sync normally.
209 synchronizePinnedStackBounds();
210 } else {
211 // If we're animating, set the current bounds to the animated bounds. That way, the
212 // touch gesture will begin at the most recent animated location of the bounds.
213 mBounds.set(mAnimatedBounds);
214 }
215 }
216
217 /**
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500218 * Tries to move the pinned stack to the given {@param bounds}.
Winson Chung2a82fe52017-02-02 14:43:34 -0800219 */
220 void movePip(Rect toBounds) {
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500221 movePip(toBounds, false /* isDragging */);
222 }
223
224 /**
225 * Tries to move the pinned stack to the given {@param bounds}.
226 *
227 * @param isDragging Whether this movement is the result of a drag touch gesture. If so, we
228 * won't notify the floating content coordinator of this move, since that will
229 * happen when the gesture ends.
230 */
231 void movePip(Rect toBounds, boolean isDragging) {
232 if (!isDragging) {
233 mFloatingContentCoordinator.onContentMoved(this);
234 }
235
Joshua Tsujif39539d2020-04-03 18:53:06 -0400236 if (!mSpringingToTouch) {
237 // If we are moving PIP directly to the touch event locations, cancel any animations and
238 // move PIP to the given bounds.
239 cancelAnimations();
240 resizePipUnchecked(toBounds);
241 mBounds.set(toBounds);
242 } else {
243 // If PIP is 'catching up' after being stuck in the dismiss target, update the animation
244 // to spring towards the new touch location.
245 mAnimatedBoundsPhysicsAnimator
246 .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
247 .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
248 .withEndActions(() -> mSpringingToTouch = false);
249
250 startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */);
251 }
252 }
253
254 /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
255 void setSpringingToTouch(boolean springingToTouch) {
256 if (springingToTouch) {
257 mAnimatedBounds.set(mBounds);
258 }
259
260 mSpringingToTouch = springingToTouch;
261 }
262
263 void prepareForAnimation() {
264 mAnimatedBounds.set(mBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800265 }
266
267 /**
268 * Resizes the pinned stack back to fullscreen.
269 */
270 void expandPip() {
Winson Chunge6385a22017-05-02 18:15:16 -0700271 expandPip(false /* skipAnimation */);
272 }
273
274 /**
275 * Resizes the pinned stack back to fullscreen.
276 */
277 void expandPip(boolean skipAnimation) {
Winson Chungbb233762017-05-15 14:20:46 -0700278 if (DEBUG) {
279 Log.d(TAG, "expandPip: skipAnimation=" + skipAnimation
280 + " callers=\n" + Debug.getCallers(5, " "));
281 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800282 cancelAnimations();
Winson Chung79f852e2017-05-04 15:06:18 -0700283 mMenuController.hideMenuWithoutResize();
Winson Chung55701472020-03-04 19:30:30 -0800284 mPipTaskOrganizer.getUpdateHandler().post(() -> {
Hongwei Wangd39583a2020-03-04 11:14:32 -0800285 mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION);
Winson Chung2a82fe52017-02-02 14:43:34 -0800286 });
287 }
288
289 /**
290 * Dismisses the pinned stack.
291 */
Eliot Courtney293d86e2019-01-08 16:42:39 +0900292 @Override
293 public void dismissPip() {
Winson Chungbb233762017-05-15 14:20:46 -0700294 if (DEBUG) {
295 Log.d(TAG, "dismissPip: callers=\n" + Debug.getCallers(5, " "));
296 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800297 cancelAnimations();
Winson Chung79f852e2017-05-04 15:06:18 -0700298 mMenuController.hideMenuWithoutResize();
Winson Chung55701472020-03-04 19:30:30 -0800299 mPipTaskOrganizer.getUpdateHandler().post(() -> {
Winson Chung2a82fe52017-02-02 14:43:34 -0800300 try {
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700301 mActivityTaskManager.removeStacksInWindowingModes(
302 new int[]{ WINDOWING_MODE_PINNED });
Winson Chung2a82fe52017-02-02 14:43:34 -0800303 } catch (RemoteException e) {
304 Log.e(TAG, "Failed to remove PiP", e);
305 }
306 });
307 }
308
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500309 /** Sets the movement bounds to use to constrain PIP position animations. */
310 void setCurrentMovementBounds(Rect movementBounds) {
311 mMovementBounds.set(movementBounds);
312 rebuildFlingConfigs();
313
314 // The movement bounds represent the area within which we can move PIP's top-left position.
315 // The allowed area for all of PIP is those bounds plus PIP's width and height.
316 mFloatingAllowedArea.set(mMovementBounds);
317 mFloatingAllowedArea.right += mBounds.width();
318 mFloatingAllowedArea.bottom += mBounds.height();
319 }
320
Winson Chung2a82fe52017-02-02 14:43:34 -0800321 /**
322 * @return the PiP bounds.
323 */
324 Rect getBounds() {
325 return mBounds;
326 }
327
328 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800329 * Flings the PiP to the closest snap target.
330 */
Joshua Tsuji2e252462020-01-02 11:27:41 -0500331 void flingToSnapTarget(
Joshua Tsujif39539d2020-04-03 18:53:06 -0400332 float velocityX, float velocityY,
333 @Nullable Runnable updateAction, @Nullable Runnable endAction) {
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500334 mAnimatedBounds.set(mBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500335 mAnimatedBoundsPhysicsAnimator
336 .flingThenSpring(
337 FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
338 true /* flingMustReachMinOrMax */)
339 .flingThenSpring(
340 FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500341 .withEndActions(endAction);
342
Joshua Tsujif39539d2020-04-03 18:53:06 -0400343 if (updateAction != null) {
344 mAnimatedBoundsPhysicsAnimator.addUpdateListener(
345 (target, values) -> updateAction.run());
346 }
347
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500348 final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
349 final float estimatedFlingYEndValue =
350 PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
351
Joshua Tsuji601dc852020-04-02 15:54:15 -0400352 startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800353 }
354
355 /**
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500356 * Animates PIP to the provided bounds, using physics animations and the given spring
357 * configuration
358 */
359 void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
360 mAnimatedBounds.set(mBounds);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500361 mAnimatedBoundsPhysicsAnimator
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500362 .spring(FloatProperties.RECT_X, bounds.left, springConfig)
363 .spring(FloatProperties.RECT_Y, bounds.top, springConfig);
Joshua Tsuji601dc852020-04-02 15:54:15 -0400364 startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500365 }
366
367 /**
368 * Animates the dismissal of the PiP off the edge of the screen.
369 */
370 void animateDismiss(float velocityX, float velocityY, @Nullable Runnable updateAction) {
Joshua Tsuji2e252462020-01-02 11:27:41 -0500371 mAnimatedBounds.set(mBounds);
372
Joshua Tsujif39539d2020-04-03 18:53:06 -0400373 // Animate off the bottom of the screen, then dismiss PIP.
Joshua Tsuji2e252462020-01-02 11:27:41 -0500374 mAnimatedBoundsPhysicsAnimator
Joshua Tsujif39539d2020-04-03 18:53:06 -0400375 .spring(FloatProperties.RECT_Y,
376 mBounds.bottom + mBounds.height(),
377 velocityY,
378 mSpringConfig)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500379 .withEndActions(this::dismissPip);
380
381 // If we were provided with an update action, run it whenever there's an update.
382 if (updateAction != null) {
383 mAnimatedBoundsPhysicsAnimator.addUpdateListener(
384 (target, values) -> updateAction.run());
Winson Chung2a82fe52017-02-02 14:43:34 -0800385 }
Joshua Tsuji2e252462020-01-02 11:27:41 -0500386
Joshua Tsujif39539d2020-04-03 18:53:06 -0400387 startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800388 }
389
390 /**
391 * Animates the PiP to the expanded state to show the menu.
392 */
393 float animateToExpandedState(Rect expandedBounds, Rect movementBounds,
Hongwei Wang7c622402020-05-07 20:45:22 -0700394 Rect expandedMovementBounds, Runnable callback) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800395 float savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds), movementBounds);
396 mSnapAlgorithm.applySnapFraction(expandedBounds, expandedMovementBounds, savedSnapFraction);
Hongwei Wang7c622402020-05-07 20:45:22 -0700397 mPostPipTransitionCallback = callback;
Winson Chungd2d90972017-02-28 11:40:41 -0800398 resizeAndAnimatePipUnchecked(expandedBounds, EXPAND_STACK_TO_MENU_DURATION);
Winson Chung2a82fe52017-02-02 14:43:34 -0800399 return savedSnapFraction;
400 }
401
402 /**
403 * Animates the PiP from the expanded state to the normal state after the menu is hidden.
404 */
405 void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800406 Rect normalMovementBounds, Rect currentMovementBounds, boolean immediate) {
Winson Chungd2d90972017-02-28 11:40:41 -0800407 if (savedSnapFraction < 0f) {
408 // If there are no saved snap fractions, then just use the current bounds
409 savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
410 currentMovementBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800411 }
Winson Chungd2d90972017-02-28 11:40:41 -0800412 mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500413
Winson Chungef4dc812017-04-11 13:31:44 -0700414 if (immediate) {
415 movePip(normalBounds);
416 } else {
417 resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
418 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800419 }
420
421 /**
Tracy Zhou43513082018-03-08 21:58:36 -0800422 * Animates the PiP to offset it from the IME or shelf.
Winson Chungbaa7b722017-03-03 21:33:44 -0800423 */
wilsonshih5c4cf522019-01-25 09:03:47 +0800424 void animateToOffset(Rect originalBounds, int offset) {
Winson Chung55701472020-03-04 19:30:30 -0800425 if (DEBUG) {
426 Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
427 + " callers=\n" + Debug.getCallers(5, " "));
428 }
Winson Chungbaa7b722017-03-03 21:33:44 -0800429 cancelAnimations();
Winson Chung55701472020-03-04 19:30:30 -0800430 mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
431 mUpdateBoundsCallback);
Winson Chungbaa7b722017-03-03 21:33:44 -0800432 }
433
434 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800435 * Cancels all existing animations.
436 */
Joshua Tsuji2e252462020-01-02 11:27:41 -0500437 private void cancelAnimations() {
438 mAnimatedBoundsPhysicsAnimator.cancel();
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500439 mAnimatingToBounds.setEmpty();
Joshua Tsujif39539d2020-04-03 18:53:06 -0400440 mSpringingToTouch = false;
Winson Chung2a82fe52017-02-02 14:43:34 -0800441 }
442
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500443 /** Set new fling configs whose min/max values respect the given movement bounds. */
444 private void rebuildFlingConfigs() {
Joshua Tsuji2e252462020-01-02 11:27:41 -0500445 mFlingConfigX = new PhysicsAnimator.FlingConfig(
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500446 DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500447 mFlingConfigY = new PhysicsAnimator.FlingConfig(
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500448 DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
Joshua Tsuji2e252462020-01-02 11:27:41 -0500449 }
450
451 /**
452 * Starts the physics animator which will update the animated PIP bounds using physics
Winson Chung55701472020-03-04 19:30:30 -0800453 * animations, as well as the TimeAnimator which will apply those bounds to PIP.
Joshua Tsuji2e252462020-01-02 11:27:41 -0500454 *
455 * This will also add end actions to the bounds animator that cancel the TimeAnimator and update
456 * the 'real' bounds to equal the final animated bounds.
457 */
Joshua Tsuji601dc852020-04-02 15:54:15 -0400458 private void startBoundsAnimator(float toX, float toY) {
Joshua Tsujif39539d2020-04-03 18:53:06 -0400459 if (!mSpringingToTouch) {
460 cancelAnimations();
461 }
Joshua Tsuji2e252462020-01-02 11:27:41 -0500462
Joshua Tsuji601dc852020-04-02 15:54:15 -0400463 // Set animatingToBounds directly to avoid allocating a new Rect, but then call
464 // setAnimatingToBounds to run the normal logic for changing animatingToBounds.
465 mAnimatingToBounds.set(
466 (int) toX,
467 (int) toY,
468 (int) toX + mBounds.width(),
469 (int) toY + mBounds.height());
470 setAnimatingToBounds(mAnimatingToBounds);
471
Joshua Tsuji2e252462020-01-02 11:27:41 -0500472 mAnimatedBoundsPhysicsAnimator
Joshua Tsuji601dc852020-04-02 15:54:15 -0400473 .withEndActions(() -> {
474 mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds);
475 mAnimatingToBounds.setEmpty();
476 })
Winson Chung55701472020-03-04 19:30:30 -0800477 .addUpdateListener(mResizePipUpdateListener)
Joshua Tsuji2e252462020-01-02 11:27:41 -0500478 .start();
Winson Chung2a82fe52017-02-02 14:43:34 -0800479 }
480
481 /**
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500482 * Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
483 * we return these bounds from
484 * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
485 */
486 private void setAnimatingToBounds(Rect bounds) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700487 mAnimatingToBounds.set(bounds);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500488 mFloatingContentCoordinator.onContentMoved(this);
489 }
490
491 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800492 * Directly resizes the PiP to the given {@param bounds}.
493 */
494 private void resizePipUnchecked(Rect toBounds) {
Winson Chungbb233762017-05-15 14:20:46 -0700495 if (DEBUG) {
496 Log.d(TAG, "resizePipUnchecked: toBounds=" + toBounds
497 + " callers=\n" + Debug.getCallers(5, " "));
498 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800499 if (!toBounds.equals(mBounds)) {
Winson Chung55701472020-03-04 19:30:30 -0800500 mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
Winson Chungd2d90972017-02-28 11:40:41 -0800501 }
502 }
503
504 /**
505 * Directly resizes the PiP to the given {@param bounds}.
506 */
507 private void resizeAndAnimatePipUnchecked(Rect toBounds, int duration) {
Winson Chungbb233762017-05-15 14:20:46 -0700508 if (DEBUG) {
509 Log.d(TAG, "resizeAndAnimatePipUnchecked: toBounds=" + toBounds
510 + " duration=" + duration + " callers=\n" + Debug.getCallers(5, " "));
511 }
Winson Chungd2d90972017-02-28 11:40:41 -0800512 if (!toBounds.equals(mBounds)) {
Winson Chung55701472020-03-04 19:30:30 -0800513 mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
Joshua Tsujic81ff3d2020-02-13 14:48:40 -0500514 setAnimatingToBounds(toBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800515 }
516 }
517
518 /**
Joshua Tsujif39539d2020-04-03 18:53:06 -0400519 * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the
520 * magnetic dismiss target so it can calculate PIP's size and position.
Mady Mellor60421c92017-03-29 15:27:37 -0700521 */
Joshua Tsujif39539d2020-04-03 18:53:06 -0400522 MagnetizedObject<Rect> getMagnetizedPip() {
523 return new MagnetizedObject<Rect>(
524 mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
525 @Override
526 public float getWidth(@NonNull Rect animatedPipBounds) {
527 return animatedPipBounds.width();
528 }
Mady Mellor60421c92017-03-29 15:27:37 -0700529
Joshua Tsujif39539d2020-04-03 18:53:06 -0400530 @Override
531 public float getHeight(@NonNull Rect animatedPipBounds) {
532 return animatedPipBounds.height();
533 }
Mady Mellor47ba1402017-04-04 17:25:43 -0700534
Joshua Tsujif39539d2020-04-03 18:53:06 -0400535 @Override
536 public void getLocationOnScreen(
537 @NonNull Rect animatedPipBounds, @NonNull int[] loc) {
538 loc[0] = animatedPipBounds.left;
539 loc[1] = animatedPipBounds.top;
540 }
541 };
Mady Mellor47ba1402017-04-04 17:25:43 -0700542 }
543
Winson Chung29a78652017-02-09 18:35:26 -0800544 public void dump(PrintWriter pw, String prefix) {
545 final String innerPrefix = prefix + " ";
546 pw.println(prefix + TAG);
547 pw.println(innerPrefix + "mBounds=" + mBounds);
548 pw.println(innerPrefix + "mStableInsets=" + mStableInsets);
549 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800550}