blob: cef1b6b1d93bca66dace98248cb69a21f5b4a852 [file] [log] [blame]
Winson73bc1592016-10-18 18:47:43 -07001/*
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
Mady Mellor637cd482017-03-21 10:39:42 -070019import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
20import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
Chenjie Yuae9fdf042018-02-15 10:19:32 -080021import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
Mady Mellor637cd482017-03-21 10:39:42 -070022
Winson Chung87e5d552017-04-05 11:49:38 -070023import android.animation.Animator;
24import android.animation.AnimatorListenerAdapter;
Mady Mellor81d40612017-03-10 15:14:10 -080025import android.animation.ValueAnimator;
26import android.animation.ValueAnimator.AnimatorUpdateListener;
Winson73bc1592016-10-18 18:47:43 -070027import android.app.IActivityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070028import android.app.IActivityTaskManager;
Winson Chung9b919412017-06-19 17:01:51 -070029import android.content.ComponentName;
Winson73bc1592016-10-18 18:47:43 -070030import android.content.Context;
Winson Chung136d1ec82017-07-18 11:16:30 -070031import android.content.res.Resources;
Mady Mellora7f69742017-02-03 11:00:20 -080032import android.graphics.Point;
Winson73bc1592016-10-18 18:47:43 -070033import android.graphics.PointF;
34import android.graphics.Rect;
Mady Mellord4e40fb2017-01-26 10:43:16 -080035import android.os.Handler;
Winson73bc1592016-10-18 18:47:43 -070036import android.os.RemoteException;
37import android.util.Log;
Mady Mellora7f69742017-02-03 11:00:20 -080038import android.util.Size;
Winson Chung655332c2016-10-31 13:14:28 -070039import android.view.IPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070040import android.view.MotionEvent;
Winson73bc1592016-10-18 18:47:43 -070041import android.view.ViewConfiguration;
Phil Weaverf00cd142017-03-03 13:44:00 -080042import android.view.accessibility.AccessibilityEvent;
43import android.view.accessibility.AccessibilityManager;
44import android.view.accessibility.AccessibilityNodeInfo;
Phil Weaver0a8caa12017-08-09 11:28:41 -070045import android.view.accessibility.AccessibilityWindowInfo;
Chenjie Yuae9fdf042018-02-15 10:19:32 -080046
Chenjie Yu52cacc62017-12-08 18:11:45 -080047import com.android.internal.os.logging.MetricsLoggerWrapper;
Winson Chungcd1ff642016-10-26 09:44:43 -070048import com.android.internal.policy.PipSnapAlgorithm;
Mady Mellora7f69742017-02-03 11:00:20 -080049import com.android.systemui.R;
Winson Chungcbb15a92018-01-25 17:46:16 +000050import com.android.systemui.shared.system.InputConsumerController;
Winson73bc1592016-10-18 18:47:43 -070051import com.android.systemui.statusbar.FlingAnimationUtils;
52
Winson Chung29a78652017-02-09 18:35:26 -080053import java.io.PrintWriter;
54
Winson73bc1592016-10-18 18:47:43 -070055/**
56 * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
57 * the PIP.
58 */
Mady Mellor8c7dc422017-05-10 12:55:06 -070059public class PipTouchHandler {
Winson73bc1592016-10-18 18:47:43 -070060 private static final String TAG = "PipTouchHandler";
Winson73bc1592016-10-18 18:47:43 -070061
Mady Mellor8c7dc422017-05-10 12:55:06 -070062 // Allow the PIP to be dragged to the edge of the screen to be minimized.
63 private static final boolean ENABLE_MINIMIZE = false;
64 // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
65 private static final boolean ENABLE_FLING_DISMISS = false;
Mady Mellor2e138782017-03-27 11:09:50 -070066
Mady Mellor5d58d252017-04-18 12:48:04 -070067 private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
Tracy Zhoue19d0512018-07-31 16:29:34 -070068 private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
Winson Chungfa7053782016-11-08 15:45:10 -080069
Winson Chung6e35ee1f2017-02-14 12:06:44 -080070 // Allow dragging the PIP to a location to close it
Eliot Courtney3dc12f12018-06-01 14:26:26 +090071 private final boolean mEnableDimissDragToEdge;
Winson73bc1592016-10-18 18:47:43 -070072 private final Context mContext;
73 private final IActivityManager mActivityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070074 private final IActivityTaskManager mActivityTaskManager;
Winson73bc1592016-10-18 18:47:43 -070075 private final ViewConfiguration mViewConfig;
Winson Chung15504af2016-11-02 18:11:36 -070076 private final PipMenuListener mMenuListener = new PipMenuListener();
Winson Chung655332c2016-10-31 13:14:28 -070077 private IPinnedStackController mPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070078
Winson Chung2a82fe52017-02-02 14:43:34 -080079 private final PipMenuActivityController mMenuController;
80 private final PipDismissViewController mDismissViewController;
Winson Chung14fefc22016-11-02 10:02:29 -070081 private final PipSnapAlgorithm mSnapAlgorithm;
Phil Weaverf00cd142017-03-03 13:44:00 -080082 private final AccessibilityManager mAccessibilityManager;
Wale Ogunwale6455e502017-04-17 14:16:43 -070083 private boolean mShowPipMenuOnAnimationEnd = false;
Winson73bc1592016-10-18 18:47:43 -070084
Winson Chung2a82fe52017-02-02 14:43:34 -080085 // The current movement bounds
86 private Rect mMovementBounds = new Rect();
87
Winson Chung0a2f34f2017-08-11 18:13:24 -070088 // The reference inset bounds, used to determine the dismiss fraction
89 private Rect mInsetBounds = new Rect();
Winson Chung2a82fe52017-02-02 14:43:34 -080090 // The reference bounds used to calculate the normal/expanded target bounds
91 private Rect mNormalBounds = new Rect();
92 private Rect mNormalMovementBounds = new Rect();
93 private Rect mExpandedBounds = new Rect();
94 private Rect mExpandedMovementBounds = new Rect();
Mady Mellora7f69742017-02-03 11:00:20 -080095 private int mExpandedShortestEdgeSize;
Winson73bc1592016-10-18 18:47:43 -070096
Winson Chungef4dc812017-04-11 13:31:44 -070097 // Used to workaround an issue where the WM rotation happens before we are notified, allowing
98 // us to send stale bounds
99 private int mDeferResizeToNormalBoundsUntilRotation = -1;
100 private int mDisplayRotation;
101
Mady Mellord4e40fb2017-01-26 10:43:16 -0800102 private Handler mHandler = new Handler();
103 private Runnable mShowDismissAffordance = new Runnable() {
104 @Override
105 public void run() {
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900106 if (mEnableDimissDragToEdge) {
Mady Mellor60421c92017-03-29 15:27:37 -0700107 mDismissViewController.showDismissTarget();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800108 }
109 }
110 };
Mady Mellor81d40612017-03-10 15:14:10 -0800111 private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
112 new AnimatorUpdateListener() {
113 @Override
114 public void onAnimationUpdate(ValueAnimator animation) {
115 updateDismissFraction();
116 }
117 };
Mady Mellord4e40fb2017-01-26 10:43:16 -0800118
Winson Chungfa7053782016-11-08 15:45:10 -0800119 // Behaviour states
Winson Chung9b919412017-06-19 17:01:51 -0700120 private int mMenuState = MENU_STATE_NONE;
Winson Chungd2d90972017-02-28 11:40:41 -0800121 private boolean mIsMinimized;
Winson Chung2a82fe52017-02-02 14:43:34 -0800122 private boolean mIsImeShowing;
123 private int mImeHeight;
Winson Chung136d1ec82017-07-18 11:16:30 -0700124 private int mImeOffset;
Tracy Zhou43513082018-03-08 21:58:36 -0800125 private boolean mIsShelfShowing;
126 private int mShelfHeight;
Winson Chung2a82fe52017-02-02 14:43:34 -0800127 private float mSavedSnapFraction = -1f;
Phil Weaverf00cd142017-03-03 13:44:00 -0800128 private boolean mSendingHoverAccessibilityEvents;
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700129 private boolean mMovementWithinMinimize;
130 private boolean mMovementWithinDismiss;
Winson73bc1592016-10-18 18:47:43 -0700131
Winson Chungfa7053782016-11-08 15:45:10 -0800132 // Touch state
133 private final PipTouchState mTouchState;
Winson73bc1592016-10-18 18:47:43 -0700134 private final FlingAnimationUtils mFlingAnimationUtils;
Winson Chungfa7053782016-11-08 15:45:10 -0800135 private final PipTouchGesture[] mGestures;
Winson Chung2a82fe52017-02-02 14:43:34 -0800136 private final PipMotionHelper mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700137
Winson Chung2a82fe52017-02-02 14:43:34 -0800138 // Temp vars
Winson Chung655332c2016-10-31 13:14:28 -0700139 private final Rect mTmpBounds = new Rect();
140
Winson73bc1592016-10-18 18:47:43 -0700141 /**
Winson Chung15504af2016-11-02 18:11:36 -0700142 * A listener for the PIP menu activity.
143 */
144 private class PipMenuListener implements PipMenuActivityController.Listener {
145 @Override
Mady Mellor637cd482017-03-21 10:39:42 -0700146 public void onPipMenuStateChanged(int menuState, boolean resize) {
147 setMenuState(menuState, resize);
Winson Chung15504af2016-11-02 18:11:36 -0700148 }
Winson Chunga29eb982016-12-14 12:01:27 -0800149
150 @Override
151 public void onPipExpand() {
152 if (!mIsMinimized) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800153 mMotionHelper.expandPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800154 }
155 }
156
157 @Override
158 public void onPipMinimize() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800159 setMinimizedStateInternal(true);
Mady Mellor81d40612017-03-10 15:14:10 -0800160 mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */);
Winson Chunga29eb982016-12-14 12:01:27 -0800161 }
162
163 @Override
164 public void onPipDismiss() {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800165 MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext,
166 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800167 mMotionHelper.dismissPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800168 }
Mady Mellor637cd482017-03-21 10:39:42 -0700169
170 @Override
171 public void onPipShowMenu() {
172 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700173 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Mady Mellor637cd482017-03-21 10:39:42 -0700174 }
Winson Chung15504af2016-11-02 18:11:36 -0700175 }
176
Winson Chungd2d90972017-02-28 11:40:41 -0800177 public PipTouchHandler(Context context, IActivityManager activityManager,
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700178 IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
Winson Chungd2d90972017-02-28 11:40:41 -0800179 InputConsumerController inputConsumerController) {
Winson73bc1592016-10-18 18:47:43 -0700180
181 // Initialize the Pip input consumer
Winson73bc1592016-10-18 18:47:43 -0700182 mContext = context;
183 mActivityManager = activityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700184 mActivityTaskManager = activityTaskManager;
Phil Weaverf00cd142017-03-03 13:44:00 -0800185 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Winson73bc1592016-10-18 18:47:43 -0700186 mViewConfig = ViewConfiguration.get(context);
Winson Chung15504af2016-11-02 18:11:36 -0700187 mMenuController = menuController;
188 mMenuController.addListener(mMenuListener);
189 mDismissViewController = new PipDismissViewController(context);
Winson Chung14fefc22016-11-02 10:02:29 -0700190 mSnapAlgorithm = new PipSnapAlgorithm(mContext);
Mady Mellor15b29c72017-06-07 14:53:58 -0700191 mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f);
Winson Chunga5acf182017-01-05 16:02:27 -0800192 mGestures = new PipTouchGesture[] {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800193 mDefaultMovementGesture
Winson Chungfa7053782016-11-08 15:45:10 -0800194 };
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700195 mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
196 mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
Winson Chungbca03112017-08-16 10:38:15 -0700197 mTouchState = new PipTouchState(mViewConfig, mHandler,
198 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
199 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
Winson Chung136d1ec82017-07-18 11:16:30 -0700200
201 Resources res = context.getResources();
202 mExpandedShortestEdgeSize = res.getDimensionPixelSize(
Mady Mellora7f69742017-02-03 11:00:20 -0800203 R.dimen.pip_expanded_shortest_edge_size);
Winson Chung136d1ec82017-07-18 11:16:30 -0700204 mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
Winson Chungd2d90972017-02-28 11:40:41 -0800205
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900206 mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
207
Winson Chungd2d90972017-02-28 11:40:41 -0800208 // Register the listener for input consumer touch events
209 inputConsumerController.setTouchListener(this::handleTouchEvent);
Phil Weaverf00cd142017-03-03 13:44:00 -0800210 inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
211 onRegistrationChanged(inputConsumerController.isRegistered());
Winson73bc1592016-10-18 18:47:43 -0700212 }
213
Winson Chung85d39982017-02-24 15:21:25 -0800214 public void setTouchEnabled(boolean enabled) {
215 mTouchState.setAllowTouches(enabled);
216 }
217
Winson Chungac52f282017-03-30 14:44:52 -0700218 public void showPictureInPictureMenu() {
219 // Only show the menu if the user isn't currently interacting with the PiP
220 if (!mTouchState.isUserInteracting()) {
Mady Mellor637cd482017-03-21 10:39:42 -0700221 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700222 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Winson Chungac52f282017-03-30 14:44:52 -0700223 }
224 }
225
Winson Chung929d4f72017-01-13 10:21:33 -0800226 public void onActivityPinned() {
Winson Chung9b919412017-06-19 17:01:51 -0700227 cleanUp();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700228 mShowPipMenuOnAnimationEnd = true;
Winson Chung929d4f72017-01-13 10:21:33 -0800229 }
230
Winson Chung9b919412017-06-19 17:01:51 -0700231 public void onActivityUnpinned(ComponentName topPipActivity) {
232 if (topPipActivity == null) {
233 // Clean up state after the last PiP activity is removed
234 cleanUp();
235 }
236 }
237
Winson Chungac52f282017-03-30 14:44:52 -0700238 public void onPinnedStackAnimationEnded() {
239 // Always synchronize the motion helper bounds once PiP animations finish
240 mMotionHelper.synchronizePinnedStackBounds();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700241
242 if (mShowPipMenuOnAnimationEnd) {
243 mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700244 mMovementBounds, true /* allowMenuTimeout */, false /* willResizeMenu */);
Wale Ogunwale6455e502017-04-17 14:16:43 -0700245 mShowPipMenuOnAnimationEnd = false;
246 }
Winson Chungac52f282017-03-30 14:44:52 -0700247 }
248
Winson Chung303c6b72016-10-24 17:12:49 -0700249 public void onConfigurationChanged() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800250 mMotionHelper.onConfigurationChanged();
251 mMotionHelper.synchronizePinnedStackBounds();
Winson Chung303c6b72016-10-24 17:12:49 -0700252 }
253
Winson Chung2a82fe52017-02-02 14:43:34 -0800254 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
255 mIsImeShowing = imeVisible;
256 mImeHeight = imeHeight;
257 }
258
Tracy Zhou43513082018-03-08 21:58:36 -0800259 public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
260 mIsShelfShowing = shelfVisible;
261 mShelfHeight = shelfHeight;
262 }
263
Winson Chungbaa7b722017-03-03 21:33:44 -0800264 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800265 boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
266 final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
267
Winson Chung2a82fe52017-02-02 14:43:34 -0800268 // Re-calculate the expanded bounds
269 mNormalBounds = normalBounds;
270 Rect normalMovementBounds = new Rect();
271 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800272 bottomOffset);
Mady Mellora7f69742017-02-03 11:00:20 -0800273
274 // Calculate the expanded size
275 float aspectRatio = (float) normalBounds.width() / normalBounds.height();
276 Point displaySize = new Point();
277 mContext.getDisplay().getRealSize(displaySize);
278 Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
279 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
280 mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Winson Chung2a82fe52017-02-02 14:43:34 -0800281 Rect expandedMovementBounds = new Rect();
282 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800283 bottomOffset);
Winson Chung2a82fe52017-02-02 14:43:34 -0800284
Tracy Zhou43513082018-03-08 21:58:36 -0800285 // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
286 // occluded by the IME or shelf.
287 if (fromImeAdjustment || fromShelfAdjustment) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800288 if (mTouchState.isUserInteracting()) {
289 // Defer the update of the current movement bounds until after the user finishes
290 // touching the screen
291 } else {
Tracy Zhou4be72d22018-03-22 11:41:00 -0700292 final int adjustedOffset = Math.max(mIsImeShowing ? mImeHeight + mImeOffset : 0,
293 mIsShelfShowing ? mShelfHeight : 0);
294 Rect normalAdjustedBounds = new Rect();
295 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalAdjustedBounds,
296 adjustedOffset);
297 Rect expandedAdjustedBounds = new Rect();
298 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds,
299 expandedAdjustedBounds, adjustedOffset);
300 final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
301 ? expandedAdjustedBounds
302 : normalAdjustedBounds;
Tracy Zhou805e66c2018-04-04 23:45:26 -0700303 final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
304 ? expandedMovementBounds
305 : normalMovementBounds;
306
307 // If the PIP window needs to shift to right above shelf/IME and it's already above
308 // that, don't move the PIP window.
309 if (toAdjustedBounds.bottom < mMovementBounds.bottom
310 && animatingBounds.top < toAdjustedBounds.bottom) {
311 return;
312 }
313
314 // If the PIP window needs to shift down due to dismissal of shelf/IME but it's way
315 // above the position as if shelf/IME shows, don't move the PIP window.
316 int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom;
317 int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight;
Tracy Zhoue19d0512018-07-31 16:29:34 -0700318 final float bottomOffsetBufferInPx = BOTTOM_OFFSET_BUFFER_DP
319 * mContext.getResources().getDisplayMetrics().density;
Tracy Zhou805e66c2018-04-04 23:45:26 -0700320 if (toAdjustedBounds.bottom >= mMovementBounds.bottom
Tracy Zhoue19d0512018-07-31 16:29:34 -0700321 && animatingBounds.top + Math.round(bottomOffsetBufferInPx)
Tracy Zhou805e66c2018-04-04 23:45:26 -0700322 < toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) {
323 return;
324 }
Tracy Zhou4be72d22018-03-22 11:41:00 -0700325
326 animateToOffset(animatingBounds, toAdjustedBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800327 }
Winson Chung14fbe142016-12-19 16:18:24 -0800328 }
Winson Chunga29eb982016-12-14 12:01:27 -0800329
Winson Chung2a82fe52017-02-02 14:43:34 -0800330 // Update the movement bounds after doing the calculations based on the old movement bounds
331 // above
332 mNormalMovementBounds = normalMovementBounds;
333 mExpandedMovementBounds = expandedMovementBounds;
Winson Chungef4dc812017-04-11 13:31:44 -0700334 mDisplayRotation = displayRotation;
Winson Chung0a2f34f2017-08-11 18:13:24 -0700335 mInsetBounds.set(insetBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700336 updateMovementBounds(mMenuState);
Winson Chungef4dc812017-04-11 13:31:44 -0700337
338 // If we have a deferred resize, apply it now
339 if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
340 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
341 mNormalMovementBounds, mMovementBounds, mIsMinimized,
342 true /* immediate */);
343 mSavedSnapFraction = -1f;
344 mDeferResizeToNormalBoundsUntilRotation = -1;
345 }
Winson Chunga29eb982016-12-14 12:01:27 -0800346 }
347
Tracy Zhou4be72d22018-03-22 11:41:00 -0700348 private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
wilsonshih5c4cf522019-01-25 09:03:47 +0800349 int offset = toAdjustedBounds.bottom - animatingBounds.top;
Tracy Zhou4b696822018-04-02 18:19:43 -0700350 // In landscape mode, PIP window can go offset while launching IME. We want to align the
351 // the top of the PIP window with the top of the movement bounds in that case.
wilsonshih5c4cf522019-01-25 09:03:47 +0800352 offset += Math.max(0, mMovementBounds.top - animatingBounds.top);
353 mMotionHelper.animateToOffset(animatingBounds, offset);
Tracy Zhou43513082018-03-08 21:58:36 -0800354 }
355
Phil Weaverf00cd142017-03-03 13:44:00 -0800356 private void onRegistrationChanged(boolean isRegistered) {
357 mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
Winson Chungfe1fa642017-03-13 10:51:22 -0700358 ? new PipAccessibilityInteractionConnection(mMotionHelper,
359 this::onAccessibilityShowMenu, mHandler) : null);
Winson Chungb54b65b2017-04-26 14:02:13 -0700360
361 if (!isRegistered && mTouchState.isUserInteracting()) {
362 // If the input consumer is unregistered while the user is interacting, then we may not
363 // get the final TOUCH_UP event, so clean up the dismiss target as well
364 cleanUpDismissTarget();
365 }
Winson Chungfe1fa642017-03-13 10:51:22 -0700366 }
367
368 private void onAccessibilityShowMenu() {
Mady Mellor637cd482017-03-21 10:39:42 -0700369 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Rhed Jao5e00c982018-11-27 19:41:05 +0800370 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Phil Weaverf00cd142017-03-03 13:44:00 -0800371 }
372
Winson Chung15504af2016-11-02 18:11:36 -0700373 private boolean handleTouchEvent(MotionEvent ev) {
Winson Chung655332c2016-10-31 13:14:28 -0700374 // Skip touch handling until we are bound to the controller
375 if (mPinnedStackController == null) {
Winson Chung15504af2016-11-02 18:11:36 -0700376 return true;
Winson Chung655332c2016-10-31 13:14:28 -0700377 }
378
Winson Chungfa7053782016-11-08 15:45:10 -0800379 // Update the touch state
380 mTouchState.onTouchEvent(ev);
381
Winson73bc1592016-10-18 18:47:43 -0700382 switch (ev.getAction()) {
383 case MotionEvent.ACTION_DOWN: {
Winson Chung2a82fe52017-02-02 14:43:34 -0800384 mMotionHelper.synchronizePinnedStackBounds();
Winson73bc1592016-10-18 18:47:43 -0700385
Winson Chungfa7053782016-11-08 15:45:10 -0800386 for (PipTouchGesture gesture : mGestures) {
387 gesture.onDown(mTouchState);
388 }
Winson73bc1592016-10-18 18:47:43 -0700389 break;
390 }
391 case MotionEvent.ACTION_MOVE: {
Winson Chungfa7053782016-11-08 15:45:10 -0800392 for (PipTouchGesture gesture : mGestures) {
393 if (gesture.onMove(mTouchState)) {
394 break;
Winson73bc1592016-10-18 18:47:43 -0700395 }
396 }
Winson73bc1592016-10-18 18:47:43 -0700397 break;
398 }
399 case MotionEvent.ACTION_UP: {
Winson Chung14fefc22016-11-02 10:02:29 -0700400 // Update the movement bounds again if the state has changed since the user started
401 // dragging (ie. when the IME shows)
Mady Mellor637cd482017-03-21 10:39:42 -0700402 updateMovementBounds(mMenuState);
Winson Chung14fefc22016-11-02 10:02:29 -0700403
Winson Chungfa7053782016-11-08 15:45:10 -0800404 for (PipTouchGesture gesture : mGestures) {
405 if (gesture.onUp(mTouchState)) {
406 break;
Winson Chung5cd26ff2016-10-24 11:50:44 -0700407 }
Winson Chung5cd26ff2016-10-24 11:50:44 -0700408 }
Winson73bc1592016-10-18 18:47:43 -0700409
410 // Fall through to clean up
411 }
412 case MotionEvent.ACTION_CANCEL: {
Winson Chung85d39982017-02-24 15:21:25 -0800413 mTouchState.reset();
Winson73bc1592016-10-18 18:47:43 -0700414 break;
415 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800416 case MotionEvent.ACTION_HOVER_ENTER:
417 case MotionEvent.ACTION_HOVER_MOVE: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000418 if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800419 AccessibilityEvent event = AccessibilityEvent.obtain(
420 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700421 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700422 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700423 event.setWindowId(
424 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800425 mAccessibilityManager.sendAccessibilityEvent(event);
426 mSendingHoverAccessibilityEvents = true;
427 }
428 break;
429 }
430 case MotionEvent.ACTION_HOVER_EXIT: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000431 if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800432 AccessibilityEvent event = AccessibilityEvent.obtain(
433 AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700434 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700435 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700436 event.setWindowId(
437 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800438 mAccessibilityManager.sendAccessibilityEvent(event);
439 mSendingHoverAccessibilityEvents = false;
440 }
441 break;
442 }
Winson73bc1592016-10-18 18:47:43 -0700443 }
Mady Mellor637cd482017-03-21 10:39:42 -0700444 return mMenuState == MENU_STATE_NONE;
Winson Chung15504af2016-11-02 18:11:36 -0700445 }
446
447 /**
Mady Mellor81d40612017-03-10 15:14:10 -0800448 * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
449 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700450 private void updateDismissFraction() {
Winson Chung0a2f34f2017-08-11 18:13:24 -0700451 // Skip updating the dismiss fraction when the IME is showing. This is to work around an
452 // issue where starting the menu activity for the dismiss overlay will steal the window
453 // focus, which closes the IME.
454 if (mMenuController != null && !mIsImeShowing) {
Mady Mellor81d40612017-03-10 15:14:10 -0800455 Rect bounds = mMotionHelper.getBounds();
Winson Chung0a2f34f2017-08-11 18:13:24 -0700456 final float target = mInsetBounds.bottom;
Mady Mellor81d40612017-03-10 15:14:10 -0800457 float fraction = 0f;
458 if (bounds.bottom > target) {
459 final float distance = bounds.bottom - target;
460 fraction = Math.min(distance / bounds.height(), 1f);
461 }
Winson Chungbca03112017-08-16 10:38:15 -0700462 if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
Winson Chung87e5d552017-04-05 11:49:38 -0700463 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
464 mMenuController.setDismissFraction(fraction);
465 }
Mady Mellor81d40612017-03-10 15:14:10 -0800466 }
467 }
468
469 /**
Winson Chunga29eb982016-12-14 12:01:27 -0800470 * Sets the controller to update the system of changes from user interaction.
471 */
472 void setPinnedStackController(IPinnedStackController controller) {
473 mPinnedStackController = controller;
474 }
475
476 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800477 * Sets the minimized state.
Winson Chungdff5c082016-11-02 17:28:03 -0700478 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700479 private void setMinimizedStateInternal(boolean isMinimized) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700480 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700481 return;
482 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800483 setMinimizedState(isMinimized, false /* fromController */);
Winson Chungdff5c082016-11-02 17:28:03 -0700484 }
485
486 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800487 * Sets the minimized state.
Winson Chungfa7053782016-11-08 15:45:10 -0800488 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800489 void setMinimizedState(boolean isMinimized, boolean fromController) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700490 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700491 return;
492 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800493 if (mIsMinimized != isMinimized) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800494 MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
495 isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800496 }
497 mIsMinimized = isMinimized;
498 mSnapAlgorithm.setMinimized(isMinimized);
Winson Chung54f0c652016-12-06 14:46:31 -0800499
Winson Chung2a82fe52017-02-02 14:43:34 -0800500 if (fromController) {
501 if (isMinimized) {
502 // Move the PiP to the new bounds immediately if minimized
503 mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
504 mMovementBounds));
505 }
506 } else if (mPinnedStackController != null) {
Winson Chung54f0c652016-12-06 14:46:31 -0800507 try {
508 mPinnedStackController.setIsMinimized(isMinimized);
509 } catch (RemoteException e) {
510 Log.e(TAG, "Could not set minimized state", e);
511 }
Winson Chungfa7053782016-11-08 15:45:10 -0800512 }
513 }
514
515 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800516 * Sets the menu visibility.
Winson Chungfa7053782016-11-08 15:45:10 -0800517 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700518 private void setMenuState(int menuState, boolean resize) {
Winson Chunga89053d2018-06-12 16:47:30 -0700519 if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800520 // Save the current snap fraction and if we do not drag or move the PiP, then
521 // we store back to this snap fraction. Otherwise, we'll reset the snap
522 // fraction and snap to the closest edge
523 Rect expandedBounds = new Rect(mExpandedBounds);
524 if (resize) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800525 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
526 mMovementBounds, mExpandedMovementBounds);
Winson Chungd2d90972017-02-28 11:40:41 -0800527 }
Winson Chunga89053d2018-06-12 16:47:30 -0700528 } else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800529 // Try and restore the PiP to the closest edge, using the saved snap fraction
530 // if possible
531 if (resize) {
Winson Chungef4dc812017-04-11 13:31:44 -0700532 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Winson Chungbb233762017-05-15 14:20:46 -0700533 // This is a very special case: when the menu is expanded and visible,
534 // navigating to another activity can trigger auto-enter PiP, and if the
535 // revealed activity has a forced rotation set, then the controller will get
536 // updated with the new rotation of the display. However, at the same time,
537 // SystemUI will try to hide the menu by creating an animation to the normal
538 // bounds which are now stale. In such a case we defer the animation to the
539 // normal bounds until after the next onMovementBoundsChanged() call to get the
540 // bounds in the new orientation
Winson Chungef4dc812017-04-11 13:31:44 -0700541 try {
542 int displayRotation = mPinnedStackController.getDisplayRotation();
543 if (mDisplayRotation != displayRotation) {
544 mDeferResizeToNormalBoundsUntilRotation = displayRotation;
545 }
546 } catch (RemoteException e) {
547 Log.e(TAG, "Could not get display rotation from controller");
548 }
549 }
550
551 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
552 Rect normalBounds = new Rect(mNormalBounds);
553 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
554 mNormalMovementBounds, mMovementBounds, mIsMinimized,
555 false /* immediate */);
556 mSavedSnapFraction = -1f;
557 }
558 } else {
Winson Chungbb233762017-05-15 14:20:46 -0700559 // If resizing is not allowed, then the PiP should be frozen until the transition
560 // ends as well
561 setTouchEnabled(false);
Winson Chungef4dc812017-04-11 13:31:44 -0700562 mSavedSnapFraction = -1f;
Winson Chunga29eb982016-12-14 12:01:27 -0800563 }
Winson Chungfa7053782016-11-08 15:45:10 -0800564 }
Mady Mellor637cd482017-03-21 10:39:42 -0700565 mMenuState = menuState;
566 updateMovementBounds(menuState);
567 if (menuState != MENU_STATE_CLOSE) {
Chenjie Yu52cacc62017-12-08 18:11:45 -0800568 MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
Mady Mellor637cd482017-03-21 10:39:42 -0700569 }
Winson Chungfa7053782016-11-08 15:45:10 -0800570 }
571
572 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800573 * @return the motion helper.
Winson Chungfa7053782016-11-08 15:45:10 -0800574 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800575 public PipMotionHelper getMotionHelper() {
576 return mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700577 }
Winson Chungfa7053782016-11-08 15:45:10 -0800578
579 /**
Winson Chungfa7053782016-11-08 15:45:10 -0800580 * Gesture controlling normal movement of the PIP.
581 */
582 private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700583 // Whether the PiP was on the left side of the screen at the start of the gesture
584 private boolean mStartedOnLeft;
Winson Chung8ec0e162017-07-07 14:49:49 -0700585 private final Point mStartPosition = new Point();
586 private final PointF mDelta = new PointF();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800587
588 @Override
589 public void onDown(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800590 if (!touchState.isUserInteracting()) {
591 return;
592 }
593
Mady Mellor15b29c72017-06-07 14:53:58 -0700594 Rect bounds = mMotionHelper.getBounds();
Winson Chung8ec0e162017-07-07 14:49:49 -0700595 mDelta.set(0f, 0f);
596 mStartPosition.set(bounds.left, bounds.top);
Mady Mellor15b29c72017-06-07 14:53:58 -0700597 mStartedOnLeft = bounds.left < mMovementBounds.centerX();
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700598 mMovementWithinMinimize = true;
599 mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
600
Mady Mellora7f69742017-02-03 11:00:20 -0800601 // If the menu is still visible, and we aren't minimized, then just poke the menu
602 // so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700603 if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
Mady Mellora7f69742017-02-03 11:00:20 -0800604 mMenuController.pokeMenu();
605 }
606
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900607 if (mEnableDimissDragToEdge) {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800608 mDismissViewController.createDismissTarget();
609 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
610 }
611 }
612
Winson Chungfa7053782016-11-08 15:45:10 -0800613 @Override
614 boolean onMove(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800615 if (!touchState.isUserInteracting()) {
616 return false;
617 }
618
Winson Chung2a82fe52017-02-02 14:43:34 -0800619 if (touchState.startedDragging()) {
620 mSavedSnapFraction = -1f;
Winson Chung2a82fe52017-02-02 14:43:34 -0800621
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900622 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700623 mHandler.removeCallbacks(mShowDismissAffordance);
624 mDismissViewController.showDismissTarget();
625 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800626 }
627
Winson Chungfa7053782016-11-08 15:45:10 -0800628 if (touchState.isDragging()) {
629 // Move the pinned stack freely
Winson Chung2a82fe52017-02-02 14:43:34 -0800630 final PointF lastDelta = touchState.getLastTouchDelta();
Winson Chung8ec0e162017-07-07 14:49:49 -0700631 float lastX = mStartPosition.x + mDelta.x;
632 float lastY = mStartPosition.y + mDelta.y;
633 float left = lastX + lastDelta.x;
634 float top = lastY + lastDelta.y;
Mady Mellor8c7dc422017-05-10 12:55:06 -0700635 if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800636 left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
Winson Chungfa7053782016-11-08 15:45:10 -0800637 }
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900638 if (mEnableDimissDragToEdge) {
Mady Mellor57d22552017-03-09 15:37:13 -0800639 // Allow pip to move past bottom bounds
640 top = Math.max(mMovementBounds.top, top);
641 } else {
642 top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
643 }
Winson Chung8ec0e162017-07-07 14:49:49 -0700644
645 // Add to the cumulative delta after bounding the position
646 mDelta.x += left - lastX;
647 mDelta.y += top - lastY;
648
649 mTmpBounds.set(mMotionHelper.getBounds());
Winson Chungfa7053782016-11-08 15:45:10 -0800650 mTmpBounds.offsetTo((int) left, (int) top);
Winson Chung2a82fe52017-02-02 14:43:34 -0800651 mMotionHelper.movePip(mTmpBounds);
652
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900653 if (mEnableDimissDragToEdge) {
Mady Mellor81d40612017-03-10 15:14:10 -0800654 updateDismissFraction();
655 }
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700656
657 final PointF curPos = touchState.getLastTouchPosition();
658 if (mMovementWithinMinimize) {
659 // Track if movement remains near starting edge to identify swipes to minimize
660 mMovementWithinMinimize = mStartedOnLeft
661 ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
662 : curPos.x >= mMovementBounds.right;
663 }
664 if (mMovementWithinDismiss) {
665 // Track if movement remains near the bottom edge to identify swipe to dismiss
666 mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
667 }
Winson Chungfa7053782016-11-08 15:45:10 -0800668 return true;
669 }
670 return false;
671 }
672
673 @Override
674 public boolean onUp(PipTouchState touchState) {
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900675 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700676 // Clean up the dismiss target regardless of the touch state in case the touch
677 // enabled state changes while the user is interacting
678 cleanUpDismissTarget();
679 }
680
Winson Chung85d39982017-02-24 15:21:25 -0800681 if (!touchState.isUserInteracting()) {
682 return false;
683 }
684
Mady Mellor60421c92017-03-29 15:27:37 -0700685 final PointF vel = touchState.getVelocity();
686 final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
687 final float velocity = PointF.length(vel.x, vel.y);
688 final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
Mady Mellor8c7dc422017-05-10 12:55:06 -0700689 final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
Mady Mellor47ba1402017-04-04 17:25:43 -0700690 && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
691 && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
692 vel.y, isFling);
693 final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
694 && (mMovementWithinDismiss || isUpWithinDimiss);
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900695 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700696 // Check if the user dragged or flung the PiP offscreen to dismiss it
697 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800698 MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
699 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chungb54b65b2017-04-26 14:02:13 -0700700 mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
701 vel.y, mUpdateScrimListener);
Winson Chungb54b65b2017-04-26 14:02:13 -0700702 return true;
Mady Mellord4e40fb2017-01-26 10:43:16 -0800703 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800704 }
Winson Chungd2d90972017-02-28 11:40:41 -0800705
Winson Chungfa7053782016-11-08 15:45:10 -0800706 if (touchState.isDragging()) {
Mady Mellor84a0f892017-03-27 14:10:46 -0700707 final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
708 && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700709 if (ENABLE_MINIMIZE &&
Mady Mellor2e138782017-03-27 11:09:50 -0700710 !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800711 // Pip should be minimized
Winson Chung2a82fe52017-02-02 14:43:34 -0800712 setMinimizedStateInternal(true);
Mady Mellor637cd482017-03-21 10:39:42 -0700713 if (mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800714 // If the user dragged the expanded PiP to the edge, then hiding the menu
715 // will trigger the PiP to be scaled back to the normal size with the
716 // minimize offset adjusted
717 mMenuController.hideMenu();
718 } else {
Mady Mellor81d40612017-03-10 15:14:10 -0800719 mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
720 mUpdateScrimListener);
Winson Chungd2d90972017-02-28 11:40:41 -0800721 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800722 return true;
723 }
724 if (mIsMinimized) {
Winson Chungd2d90972017-02-28 11:40:41 -0800725 // If we're dragging and it wasn't a minimize gesture then we shouldn't be
726 // minimized.
Winson Chung2a82fe52017-02-02 14:43:34 -0800727 setMinimizedStateInternal(false);
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800728 }
729
Winson Chung87e5d552017-04-05 11:49:38 -0700730 AnimatorListenerAdapter postAnimationCallback = null;
Mady Mellor637cd482017-03-21 10:39:42 -0700731 if (mMenuState != MENU_STATE_NONE) {
Winson Chung87e5d552017-04-05 11:49:38 -0700732 // If the menu is still visible, and we aren't minimized, then just poke the
733 // menu so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700734 mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700735 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Winson Chung87e5d552017-04-05 11:49:38 -0700736 } else {
737 // If the menu is not visible, then we can still be showing the activity for the
738 // dismiss overlay, so just finish it after the animation completes
739 postAnimationCallback = new AnimatorListenerAdapter() {
740 @Override
741 public void onAnimationEnd(Animator animation) {
742 mMenuController.hideMenu();
743 }
744 };
Winson Chungd2d90972017-02-28 11:40:41 -0800745 }
746
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700747 if (isFling) {
Mady Mellor81d40612017-03-10 15:14:10 -0800748 mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
Mady Mellor15b29c72017-06-07 14:53:58 -0700749 mUpdateScrimListener, postAnimationCallback,
750 mStartPosition);
Winson Chungfa7053782016-11-08 15:45:10 -0800751 } else {
Winson Chung87e5d552017-04-05 11:49:38 -0700752 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
753 postAnimationCallback);
Winson Chungfa7053782016-11-08 15:45:10 -0800754 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800755 } else if (mIsMinimized) {
756 // This was a tap, so no longer minimized
Winson Chung87e5d552017-04-05 11:49:38 -0700757 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */,
758 null /* animatorListener */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800759 setMinimizedStateInternal(false);
Mady Mellor637cd482017-03-21 10:39:42 -0700760 } else if (mMenuState != MENU_STATE_FULL) {
Winson Chungbca03112017-08-16 10:38:15 -0700761 if (mTouchState.isDoubleTap()) {
762 // Expand to fullscreen if this is a double tap
763 mMotionHelper.expandPip();
764 } else if (!mTouchState.isWaitingForDoubleTap()) {
765 // User has stalled long enough for this not to be a drag or a double tap, just
766 // expand the menu
767 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
768 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
769 } else {
770 // Next touch event _may_ be the second tap for the double-tap, schedule a
771 // fallback runnable to trigger the menu if no touch event occurs before the
772 // next tap
773 mTouchState.scheduleDoubleTapTimeoutCallback();
774 }
Winson Chungfa7053782016-11-08 15:45:10 -0800775 } else {
Winson Chungbe4a8082017-04-13 13:52:04 -0700776 mMenuController.hideMenu();
Winson Chung2a82fe52017-02-02 14:43:34 -0800777 mMotionHelper.expandPip();
Winson Chungfa7053782016-11-08 15:45:10 -0800778 }
779 return true;
780 }
781 };
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800782
783 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800784 * Updates the current movement bounds based on whether the menu is currently visible.
785 */
Mady Mellor637cd482017-03-21 10:39:42 -0700786 private void updateMovementBounds(int menuState) {
Winson Chunga71febe2017-05-22 11:14:22 -0700787 boolean isMenuExpanded = menuState == MENU_STATE_FULL;
788 mMovementBounds = isMenuExpanded
Winson Chung2a82fe52017-02-02 14:43:34 -0800789 ? mExpandedMovementBounds
790 : mNormalMovementBounds;
Winson Chunga71febe2017-05-22 11:14:22 -0700791 try {
792 mPinnedStackController.setMinEdgeSize(isMenuExpanded ? mExpandedShortestEdgeSize : 0);
793 } catch (RemoteException e) {
794 Log.e(TAG, "Could not set minimized state", e);
795 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800796 }
Winson Chung29a78652017-02-09 18:35:26 -0800797
Winson Chungb54b65b2017-04-26 14:02:13 -0700798 /**
799 * Removes the dismiss target and cancels any pending callbacks to show it.
800 */
801 private void cleanUpDismissTarget() {
802 mHandler.removeCallbacks(mShowDismissAffordance);
803 mDismissViewController.destroyDismissTarget();
804 }
805
Winson Chung9b919412017-06-19 17:01:51 -0700806 /**
807 * Resets some states related to the touch handling.
808 */
809 private void cleanUp() {
810 if (mIsMinimized) {
811 setMinimizedStateInternal(false);
812 }
813 cleanUpDismissTarget();
814 }
815
Winson Chungbb787442017-09-01 11:33:47 -0700816 /**
817 * @return whether the menu will resize as a part of showing the full menu.
818 */
819 private boolean willResizeMenu() {
820 return mExpandedBounds.width() != mNormalBounds.width() ||
821 mExpandedBounds.height() != mNormalBounds.height();
822 }
823
Winson Chung29a78652017-02-09 18:35:26 -0800824 public void dump(PrintWriter pw, String prefix) {
825 final String innerPrefix = prefix + " ";
826 pw.println(prefix + TAG);
827 pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
828 pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
829 pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
830 pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
831 pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700832 pw.println(innerPrefix + "mMenuState=" + mMenuState);
Winson Chungd2d90972017-02-28 11:40:41 -0800833 pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
Winson Chung29a78652017-02-09 18:35:26 -0800834 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
835 pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
Tracy Zhou43513082018-03-08 21:58:36 -0800836 pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
837 pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
Winson Chung29a78652017-02-09 18:35:26 -0800838 pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900839 pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700840 pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
Winson Chung29a78652017-02-09 18:35:26 -0800841 mSnapAlgorithm.dump(pw, innerPrefix);
842 mTouchState.dump(pw, innerPrefix);
843 mMotionHelper.dump(pw, innerPrefix);
844 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800845
Winson73bc1592016-10-18 18:47:43 -0700846}