blob: 30cf412671bc54aec6867d9d3b4a39896aa75570 [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;
Tracy Zhou535c2122019-02-21 17:02:47 -080040import android.view.InputEvent;
Winson73bc1592016-10-18 18:47:43 -070041import android.view.MotionEvent;
Winson73bc1592016-10-18 18:47:43 -070042import android.view.ViewConfiguration;
Phil Weaverf00cd142017-03-03 13:44:00 -080043import android.view.accessibility.AccessibilityEvent;
44import android.view.accessibility.AccessibilityManager;
45import android.view.accessibility.AccessibilityNodeInfo;
Phil Weaver0a8caa12017-08-09 11:28:41 -070046import android.view.accessibility.AccessibilityWindowInfo;
Chenjie Yuae9fdf042018-02-15 10:19:32 -080047
Chenjie Yu52cacc62017-12-08 18:11:45 -080048import com.android.internal.os.logging.MetricsLoggerWrapper;
Winson Chungcd1ff642016-10-26 09:44:43 -070049import com.android.internal.policy.PipSnapAlgorithm;
Mady Mellora7f69742017-02-03 11:00:20 -080050import com.android.systemui.R;
Winson Chungcbb15a92018-01-25 17:46:16 +000051import com.android.systemui.shared.system.InputConsumerController;
Winson73bc1592016-10-18 18:47:43 -070052import com.android.systemui.statusbar.FlingAnimationUtils;
53
Winson Chung29a78652017-02-09 18:35:26 -080054import java.io.PrintWriter;
55
Winson73bc1592016-10-18 18:47:43 -070056/**
57 * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
58 * the PIP.
59 */
Mady Mellor8c7dc422017-05-10 12:55:06 -070060public class PipTouchHandler {
Winson73bc1592016-10-18 18:47:43 -070061 private static final String TAG = "PipTouchHandler";
Winson73bc1592016-10-18 18:47:43 -070062
Mady Mellor8c7dc422017-05-10 12:55:06 -070063 // Allow the PIP to be dragged to the edge of the screen to be minimized.
64 private static final boolean ENABLE_MINIMIZE = false;
65 // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
66 private static final boolean ENABLE_FLING_DISMISS = false;
Mady Mellor2e138782017-03-27 11:09:50 -070067
Mady Mellor5d58d252017-04-18 12:48:04 -070068 private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
Tracy Zhoue19d0512018-07-31 16:29:34 -070069 private static final int BOTTOM_OFFSET_BUFFER_DP = 1;
Winson Chungfa7053782016-11-08 15:45:10 -080070
Winson Chung6e35ee1f2017-02-14 12:06:44 -080071 // Allow dragging the PIP to a location to close it
Eliot Courtney3dc12f12018-06-01 14:26:26 +090072 private final boolean mEnableDimissDragToEdge;
Winson73bc1592016-10-18 18:47:43 -070073 private final Context mContext;
74 private final IActivityManager mActivityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -070075 private final IActivityTaskManager mActivityTaskManager;
Winson73bc1592016-10-18 18:47:43 -070076 private final ViewConfiguration mViewConfig;
Winson Chung15504af2016-11-02 18:11:36 -070077 private final PipMenuListener mMenuListener = new PipMenuListener();
Winson Chung655332c2016-10-31 13:14:28 -070078 private IPinnedStackController mPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070079
Winson Chung2a82fe52017-02-02 14:43:34 -080080 private final PipMenuActivityController mMenuController;
81 private final PipDismissViewController mDismissViewController;
Winson Chung14fefc22016-11-02 10:02:29 -070082 private final PipSnapAlgorithm mSnapAlgorithm;
Phil Weaverf00cd142017-03-03 13:44:00 -080083 private final AccessibilityManager mAccessibilityManager;
Wale Ogunwale6455e502017-04-17 14:16:43 -070084 private boolean mShowPipMenuOnAnimationEnd = false;
Winson73bc1592016-10-18 18:47:43 -070085
Winson Chung2a82fe52017-02-02 14:43:34 -080086 // The current movement bounds
87 private Rect mMovementBounds = new Rect();
88
Winson Chung0a2f34f2017-08-11 18:13:24 -070089 // The reference inset bounds, used to determine the dismiss fraction
90 private Rect mInsetBounds = new Rect();
Winson Chung2a82fe52017-02-02 14:43:34 -080091 // The reference bounds used to calculate the normal/expanded target bounds
92 private Rect mNormalBounds = new Rect();
93 private Rect mNormalMovementBounds = new Rect();
94 private Rect mExpandedBounds = new Rect();
95 private Rect mExpandedMovementBounds = new Rect();
Mady Mellora7f69742017-02-03 11:00:20 -080096 private int mExpandedShortestEdgeSize;
Winson73bc1592016-10-18 18:47:43 -070097
Winson Chungef4dc812017-04-11 13:31:44 -070098 // Used to workaround an issue where the WM rotation happens before we are notified, allowing
99 // us to send stale bounds
100 private int mDeferResizeToNormalBoundsUntilRotation = -1;
101 private int mDisplayRotation;
102
Mady Mellord4e40fb2017-01-26 10:43:16 -0800103 private Handler mHandler = new Handler();
104 private Runnable mShowDismissAffordance = new Runnable() {
105 @Override
106 public void run() {
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900107 if (mEnableDimissDragToEdge) {
Mady Mellor60421c92017-03-29 15:27:37 -0700108 mDismissViewController.showDismissTarget();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800109 }
110 }
111 };
Mady Mellor81d40612017-03-10 15:14:10 -0800112 private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
113 new AnimatorUpdateListener() {
114 @Override
115 public void onAnimationUpdate(ValueAnimator animation) {
116 updateDismissFraction();
117 }
118 };
Mady Mellord4e40fb2017-01-26 10:43:16 -0800119
Winson Chungfa7053782016-11-08 15:45:10 -0800120 // Behaviour states
Winson Chung9b919412017-06-19 17:01:51 -0700121 private int mMenuState = MENU_STATE_NONE;
Winson Chungd2d90972017-02-28 11:40:41 -0800122 private boolean mIsMinimized;
Winson Chung2a82fe52017-02-02 14:43:34 -0800123 private boolean mIsImeShowing;
124 private int mImeHeight;
Winson Chung136d1ec82017-07-18 11:16:30 -0700125 private int mImeOffset;
Tracy Zhou43513082018-03-08 21:58:36 -0800126 private boolean mIsShelfShowing;
127 private int mShelfHeight;
Winson Chungdec1a662019-06-04 21:20:41 -0700128 private int mMovementBoundsExtraOffsets;
Winson Chung2a82fe52017-02-02 14:43:34 -0800129 private float mSavedSnapFraction = -1f;
Phil Weaverf00cd142017-03-03 13:44:00 -0800130 private boolean mSendingHoverAccessibilityEvents;
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700131 private boolean mMovementWithinMinimize;
132 private boolean mMovementWithinDismiss;
Winson73bc1592016-10-18 18:47:43 -0700133
Winson Chungfa7053782016-11-08 15:45:10 -0800134 // Touch state
135 private final PipTouchState mTouchState;
Winson73bc1592016-10-18 18:47:43 -0700136 private final FlingAnimationUtils mFlingAnimationUtils;
Winson Chungfa7053782016-11-08 15:45:10 -0800137 private final PipTouchGesture[] mGestures;
Winson Chung2a82fe52017-02-02 14:43:34 -0800138 private final PipMotionHelper mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700139
Winson Chung2a82fe52017-02-02 14:43:34 -0800140 // Temp vars
Winson Chung655332c2016-10-31 13:14:28 -0700141 private final Rect mTmpBounds = new Rect();
142
Winson73bc1592016-10-18 18:47:43 -0700143 /**
Winson Chung15504af2016-11-02 18:11:36 -0700144 * A listener for the PIP menu activity.
145 */
146 private class PipMenuListener implements PipMenuActivityController.Listener {
147 @Override
Mady Mellor637cd482017-03-21 10:39:42 -0700148 public void onPipMenuStateChanged(int menuState, boolean resize) {
149 setMenuState(menuState, resize);
Winson Chung15504af2016-11-02 18:11:36 -0700150 }
Winson Chunga29eb982016-12-14 12:01:27 -0800151
152 @Override
153 public void onPipExpand() {
154 if (!mIsMinimized) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800155 mMotionHelper.expandPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800156 }
157 }
158
159 @Override
160 public void onPipMinimize() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800161 setMinimizedStateInternal(true);
Mady Mellor81d40612017-03-10 15:14:10 -0800162 mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */);
Winson Chunga29eb982016-12-14 12:01:27 -0800163 }
164
165 @Override
166 public void onPipDismiss() {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800167 MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext,
168 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800169 mMotionHelper.dismissPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800170 }
Mady Mellor637cd482017-03-21 10:39:42 -0700171
172 @Override
173 public void onPipShowMenu() {
174 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700175 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Mady Mellor637cd482017-03-21 10:39:42 -0700176 }
Winson Chung15504af2016-11-02 18:11:36 -0700177 }
178
Winson Chungd2d90972017-02-28 11:40:41 -0800179 public PipTouchHandler(Context context, IActivityManager activityManager,
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700180 IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
Winson Chungd2d90972017-02-28 11:40:41 -0800181 InputConsumerController inputConsumerController) {
Winson73bc1592016-10-18 18:47:43 -0700182
183 // Initialize the Pip input consumer
Winson73bc1592016-10-18 18:47:43 -0700184 mContext = context;
185 mActivityManager = activityManager;
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700186 mActivityTaskManager = activityTaskManager;
Phil Weaverf00cd142017-03-03 13:44:00 -0800187 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Winson73bc1592016-10-18 18:47:43 -0700188 mViewConfig = ViewConfiguration.get(context);
Winson Chung15504af2016-11-02 18:11:36 -0700189 mMenuController = menuController;
190 mMenuController.addListener(mMenuListener);
191 mDismissViewController = new PipDismissViewController(context);
Winson Chung14fefc22016-11-02 10:02:29 -0700192 mSnapAlgorithm = new PipSnapAlgorithm(mContext);
Mady Mellor15b29c72017-06-07 14:53:58 -0700193 mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f);
Winson Chunga5acf182017-01-05 16:02:27 -0800194 mGestures = new PipTouchGesture[] {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800195 mDefaultMovementGesture
Winson Chungfa7053782016-11-08 15:45:10 -0800196 };
Wale Ogunwale65ebd952018-04-25 15:41:44 -0700197 mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
198 mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
Winson Chungbca03112017-08-16 10:38:15 -0700199 mTouchState = new PipTouchState(mViewConfig, mHandler,
200 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
201 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
Winson Chung136d1ec82017-07-18 11:16:30 -0700202
203 Resources res = context.getResources();
204 mExpandedShortestEdgeSize = res.getDimensionPixelSize(
Mady Mellora7f69742017-02-03 11:00:20 -0800205 R.dimen.pip_expanded_shortest_edge_size);
Winson Chung136d1ec82017-07-18 11:16:30 -0700206 mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
Winson Chungd2d90972017-02-28 11:40:41 -0800207
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900208 mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
209
Winson Chungd2d90972017-02-28 11:40:41 -0800210 // Register the listener for input consumer touch events
Tracy Zhou535c2122019-02-21 17:02:47 -0800211 inputConsumerController.setInputListener(this::handleTouchEvent);
Phil Weaverf00cd142017-03-03 13:44:00 -0800212 inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
213 onRegistrationChanged(inputConsumerController.isRegistered());
Winson73bc1592016-10-18 18:47:43 -0700214 }
215
Winson Chung85d39982017-02-24 15:21:25 -0800216 public void setTouchEnabled(boolean enabled) {
217 mTouchState.setAllowTouches(enabled);
218 }
219
Winson Chungac52f282017-03-30 14:44:52 -0700220 public void showPictureInPictureMenu() {
221 // Only show the menu if the user isn't currently interacting with the PiP
222 if (!mTouchState.isUserInteracting()) {
Mady Mellor637cd482017-03-21 10:39:42 -0700223 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700224 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Winson Chungac52f282017-03-30 14:44:52 -0700225 }
226 }
227
Winson Chung929d4f72017-01-13 10:21:33 -0800228 public void onActivityPinned() {
Winson Chung9b919412017-06-19 17:01:51 -0700229 cleanUp();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700230 mShowPipMenuOnAnimationEnd = true;
Winson Chung929d4f72017-01-13 10:21:33 -0800231 }
232
Winson Chung9b919412017-06-19 17:01:51 -0700233 public void onActivityUnpinned(ComponentName topPipActivity) {
234 if (topPipActivity == null) {
235 // Clean up state after the last PiP activity is removed
236 cleanUp();
237 }
238 }
239
Winson Chungac52f282017-03-30 14:44:52 -0700240 public void onPinnedStackAnimationEnded() {
241 // Always synchronize the motion helper bounds once PiP animations finish
242 mMotionHelper.synchronizePinnedStackBounds();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700243
244 if (mShowPipMenuOnAnimationEnd) {
245 mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700246 mMovementBounds, true /* allowMenuTimeout */, false /* willResizeMenu */);
Wale Ogunwale6455e502017-04-17 14:16:43 -0700247 mShowPipMenuOnAnimationEnd = false;
248 }
Winson Chungac52f282017-03-30 14:44:52 -0700249 }
250
Winson Chung303c6b72016-10-24 17:12:49 -0700251 public void onConfigurationChanged() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800252 mMotionHelper.onConfigurationChanged();
253 mMotionHelper.synchronizePinnedStackBounds();
Winson Chung303c6b72016-10-24 17:12:49 -0700254 }
255
Winson Chung2a82fe52017-02-02 14:43:34 -0800256 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
257 mIsImeShowing = imeVisible;
258 mImeHeight = imeHeight;
259 }
260
Tracy Zhou43513082018-03-08 21:58:36 -0800261 public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
262 mIsShelfShowing = shelfVisible;
263 mShelfHeight = shelfHeight;
264 }
265
Winson Chungdec1a662019-06-04 21:20:41 -0700266 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect curBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800267 boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
268 final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
269
Winson Chung2a82fe52017-02-02 14:43:34 -0800270 // Re-calculate the expanded bounds
271 mNormalBounds = normalBounds;
272 Rect normalMovementBounds = new Rect();
273 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800274 bottomOffset);
Mady Mellora7f69742017-02-03 11:00:20 -0800275
276 // Calculate the expanded size
277 float aspectRatio = (float) normalBounds.width() / normalBounds.height();
278 Point displaySize = new Point();
279 mContext.getDisplay().getRealSize(displaySize);
280 Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
281 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
282 mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Winson Chung2a82fe52017-02-02 14:43:34 -0800283 Rect expandedMovementBounds = new Rect();
284 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800285 bottomOffset);
Winson Chung2a82fe52017-02-02 14:43:34 -0800286
Winson Chungdec1a662019-06-04 21:20:41 -0700287 // The extra offset does not really affect the movement bounds, but are applied based on the
288 // current state (ime showing, or shelf offset) when we need to actually shift
289 int extraOffset = Math.max(
290 mIsImeShowing ? mImeOffset : 0,
291 !mIsImeShowing && mIsShelfShowing ? mShelfHeight : 0);
292
Tracy Zhou43513082018-03-08 21:58:36 -0800293 // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
294 // occluded by the IME or shelf.
295 if (fromImeAdjustment || fromShelfAdjustment) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800296 if (mTouchState.isUserInteracting()) {
297 // Defer the update of the current movement bounds until after the user finishes
298 // touching the screen
299 } else {
Winson Chungdec1a662019-06-04 21:20:41 -0700300 final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
Tracy Zhoue19d0512018-07-31 16:29:34 -0700301 * mContext.getResources().getDisplayMetrics().density;
Winson Chungdec1a662019-06-04 21:20:41 -0700302 final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
303 ? new Rect(expandedMovementBounds)
304 : new Rect(normalMovementBounds);
305 final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
306 final int toBottom = toMovementBounds.bottom < toMovementBounds.top
307 ? toMovementBounds.bottom
308 : toMovementBounds.bottom - extraOffset;
309 if ((Math.min(prevBottom, toBottom) - offsetBufferPx) <= curBounds.top
310 && curBounds.top <= (Math.max(prevBottom, toBottom) + offsetBufferPx)) {
311 mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
Tracy Zhou805e66c2018-04-04 23:45:26 -0700312 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800313 }
Winson Chung14fbe142016-12-19 16:18:24 -0800314 }
Winson Chunga29eb982016-12-14 12:01:27 -0800315
Winson Chung2a82fe52017-02-02 14:43:34 -0800316 // Update the movement bounds after doing the calculations based on the old movement bounds
317 // above
318 mNormalMovementBounds = normalMovementBounds;
319 mExpandedMovementBounds = expandedMovementBounds;
Winson Chungef4dc812017-04-11 13:31:44 -0700320 mDisplayRotation = displayRotation;
Winson Chung0a2f34f2017-08-11 18:13:24 -0700321 mInsetBounds.set(insetBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700322 updateMovementBounds(mMenuState);
Winson Chungdec1a662019-06-04 21:20:41 -0700323 mMovementBoundsExtraOffsets = extraOffset;
Winson Chungef4dc812017-04-11 13:31:44 -0700324
325 // If we have a deferred resize, apply it now
326 if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
327 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
328 mNormalMovementBounds, mMovementBounds, mIsMinimized,
329 true /* immediate */);
330 mSavedSnapFraction = -1f;
331 mDeferResizeToNormalBoundsUntilRotation = -1;
332 }
Winson Chunga29eb982016-12-14 12:01:27 -0800333 }
334
Phil Weaverf00cd142017-03-03 13:44:00 -0800335 private void onRegistrationChanged(boolean isRegistered) {
336 mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
Winson Chungfe1fa642017-03-13 10:51:22 -0700337 ? new PipAccessibilityInteractionConnection(mMotionHelper,
338 this::onAccessibilityShowMenu, mHandler) : null);
Winson Chungb54b65b2017-04-26 14:02:13 -0700339
340 if (!isRegistered && mTouchState.isUserInteracting()) {
341 // If the input consumer is unregistered while the user is interacting, then we may not
342 // get the final TOUCH_UP event, so clean up the dismiss target as well
343 cleanUpDismissTarget();
344 }
Winson Chungfe1fa642017-03-13 10:51:22 -0700345 }
346
347 private void onAccessibilityShowMenu() {
Mady Mellor637cd482017-03-21 10:39:42 -0700348 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Rhed Jao5e00c982018-11-27 19:41:05 +0800349 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Phil Weaverf00cd142017-03-03 13:44:00 -0800350 }
351
Tracy Zhou535c2122019-02-21 17:02:47 -0800352 private boolean handleTouchEvent(InputEvent inputEvent) {
353 // Skip any non motion events
354 if (!(inputEvent instanceof MotionEvent)) {
355 return true;
356 }
Winson Chung655332c2016-10-31 13:14:28 -0700357 // Skip touch handling until we are bound to the controller
358 if (mPinnedStackController == null) {
Winson Chung15504af2016-11-02 18:11:36 -0700359 return true;
Winson Chung655332c2016-10-31 13:14:28 -0700360 }
Tracy Zhou535c2122019-02-21 17:02:47 -0800361 MotionEvent ev = (MotionEvent) inputEvent;
Winson Chung655332c2016-10-31 13:14:28 -0700362
Winson Chungfa7053782016-11-08 15:45:10 -0800363 // Update the touch state
364 mTouchState.onTouchEvent(ev);
365
Arthur Hung0ede6e92019-08-30 16:06:59 +0800366 boolean shouldDeliverToMenu = mMenuState != MENU_STATE_NONE;
367
Winson73bc1592016-10-18 18:47:43 -0700368 switch (ev.getAction()) {
369 case MotionEvent.ACTION_DOWN: {
Winson Chung2a82fe52017-02-02 14:43:34 -0800370 mMotionHelper.synchronizePinnedStackBounds();
Winson73bc1592016-10-18 18:47:43 -0700371
Winson Chungfa7053782016-11-08 15:45:10 -0800372 for (PipTouchGesture gesture : mGestures) {
373 gesture.onDown(mTouchState);
374 }
Winson73bc1592016-10-18 18:47:43 -0700375 break;
376 }
377 case MotionEvent.ACTION_MOVE: {
Winson Chungfa7053782016-11-08 15:45:10 -0800378 for (PipTouchGesture gesture : mGestures) {
379 if (gesture.onMove(mTouchState)) {
380 break;
Winson73bc1592016-10-18 18:47:43 -0700381 }
382 }
Arthur Hung0ede6e92019-08-30 16:06:59 +0800383
384 shouldDeliverToMenu = !mTouchState.isDragging();
Winson73bc1592016-10-18 18:47:43 -0700385 break;
386 }
387 case MotionEvent.ACTION_UP: {
Winson Chung14fefc22016-11-02 10:02:29 -0700388 // Update the movement bounds again if the state has changed since the user started
389 // dragging (ie. when the IME shows)
Mady Mellor637cd482017-03-21 10:39:42 -0700390 updateMovementBounds(mMenuState);
Winson Chung14fefc22016-11-02 10:02:29 -0700391
Winson Chungfa7053782016-11-08 15:45:10 -0800392 for (PipTouchGesture gesture : mGestures) {
393 if (gesture.onUp(mTouchState)) {
394 break;
Winson Chung5cd26ff2016-10-24 11:50:44 -0700395 }
Winson Chung5cd26ff2016-10-24 11:50:44 -0700396 }
Winson73bc1592016-10-18 18:47:43 -0700397
398 // Fall through to clean up
399 }
400 case MotionEvent.ACTION_CANCEL: {
Arthur Hung0ede6e92019-08-30 16:06:59 +0800401 shouldDeliverToMenu = !mTouchState.startedDragging() && !mTouchState.isDragging();
Winson Chung85d39982017-02-24 15:21:25 -0800402 mTouchState.reset();
Winson73bc1592016-10-18 18:47:43 -0700403 break;
404 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800405 case MotionEvent.ACTION_HOVER_ENTER:
406 case MotionEvent.ACTION_HOVER_MOVE: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000407 if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800408 AccessibilityEvent event = AccessibilityEvent.obtain(
409 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700410 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700411 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700412 event.setWindowId(
413 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800414 mAccessibilityManager.sendAccessibilityEvent(event);
415 mSendingHoverAccessibilityEvents = true;
416 }
417 break;
418 }
419 case MotionEvent.ACTION_HOVER_EXIT: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000420 if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800421 AccessibilityEvent event = AccessibilityEvent.obtain(
422 AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700423 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700424 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700425 event.setWindowId(
426 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800427 mAccessibilityManager.sendAccessibilityEvent(event);
428 mSendingHoverAccessibilityEvents = false;
429 }
430 break;
431 }
Winson73bc1592016-10-18 18:47:43 -0700432 }
Arthur Hung0ede6e92019-08-30 16:06:59 +0800433
434 // Deliver the event to PipMenuActivity to handle button click if the menu has shown.
435 if (shouldDeliverToMenu) {
436 final MotionEvent cloneEvent = MotionEvent.obtain(ev);
437 // Send the cancel event and cancel menu timeout if it starts to drag.
438 if (mTouchState.startedDragging()) {
439 cloneEvent.setAction(MotionEvent.ACTION_CANCEL);
440 mMenuController.pokeMenu();
441 }
442
443 mMenuController.handleTouchEvent(cloneEvent);
444 }
445
446 return true;
Winson Chung15504af2016-11-02 18:11:36 -0700447 }
448
449 /**
Mady Mellor81d40612017-03-10 15:14:10 -0800450 * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
451 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700452 private void updateDismissFraction() {
Winson Chung0a2f34f2017-08-11 18:13:24 -0700453 // Skip updating the dismiss fraction when the IME is showing. This is to work around an
454 // issue where starting the menu activity for the dismiss overlay will steal the window
455 // focus, which closes the IME.
456 if (mMenuController != null && !mIsImeShowing) {
Mady Mellor81d40612017-03-10 15:14:10 -0800457 Rect bounds = mMotionHelper.getBounds();
Winson Chung0a2f34f2017-08-11 18:13:24 -0700458 final float target = mInsetBounds.bottom;
Mady Mellor81d40612017-03-10 15:14:10 -0800459 float fraction = 0f;
460 if (bounds.bottom > target) {
461 final float distance = bounds.bottom - target;
462 fraction = Math.min(distance / bounds.height(), 1f);
463 }
Winson Chungbca03112017-08-16 10:38:15 -0700464 if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
Winson Chung87e5d552017-04-05 11:49:38 -0700465 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
466 mMenuController.setDismissFraction(fraction);
467 }
Mady Mellor81d40612017-03-10 15:14:10 -0800468 }
469 }
470
471 /**
Winson Chunga29eb982016-12-14 12:01:27 -0800472 * Sets the controller to update the system of changes from user interaction.
473 */
474 void setPinnedStackController(IPinnedStackController controller) {
475 mPinnedStackController = controller;
476 }
477
478 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800479 * Sets the minimized state.
Winson Chungdff5c082016-11-02 17:28:03 -0700480 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700481 private void setMinimizedStateInternal(boolean isMinimized) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700482 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700483 return;
484 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800485 setMinimizedState(isMinimized, false /* fromController */);
Winson Chungdff5c082016-11-02 17:28:03 -0700486 }
487
488 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800489 * Sets the minimized state.
Winson Chungfa7053782016-11-08 15:45:10 -0800490 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800491 void setMinimizedState(boolean isMinimized, boolean fromController) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700492 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700493 return;
494 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800495 if (mIsMinimized != isMinimized) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800496 MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
497 isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800498 }
499 mIsMinimized = isMinimized;
500 mSnapAlgorithm.setMinimized(isMinimized);
Winson Chung54f0c652016-12-06 14:46:31 -0800501
Winson Chung2a82fe52017-02-02 14:43:34 -0800502 if (fromController) {
503 if (isMinimized) {
504 // Move the PiP to the new bounds immediately if minimized
505 mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
506 mMovementBounds));
507 }
508 } else if (mPinnedStackController != null) {
Winson Chung54f0c652016-12-06 14:46:31 -0800509 try {
510 mPinnedStackController.setIsMinimized(isMinimized);
511 } catch (RemoteException e) {
512 Log.e(TAG, "Could not set minimized state", e);
513 }
Winson Chungfa7053782016-11-08 15:45:10 -0800514 }
515 }
516
517 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800518 * Sets the menu visibility.
Winson Chungfa7053782016-11-08 15:45:10 -0800519 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700520 private void setMenuState(int menuState, boolean resize) {
Winson Chunga89053d2018-06-12 16:47:30 -0700521 if (menuState == MENU_STATE_FULL && mMenuState != MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800522 // Save the current snap fraction and if we do not drag or move the PiP, then
523 // we store back to this snap fraction. Otherwise, we'll reset the snap
524 // fraction and snap to the closest edge
525 Rect expandedBounds = new Rect(mExpandedBounds);
526 if (resize) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800527 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
528 mMovementBounds, mExpandedMovementBounds);
Winson Chungd2d90972017-02-28 11:40:41 -0800529 }
Winson Chunga89053d2018-06-12 16:47:30 -0700530 } else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800531 // Try and restore the PiP to the closest edge, using the saved snap fraction
532 // if possible
533 if (resize) {
Winson Chungef4dc812017-04-11 13:31:44 -0700534 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Winson Chungbb233762017-05-15 14:20:46 -0700535 // This is a very special case: when the menu is expanded and visible,
536 // navigating to another activity can trigger auto-enter PiP, and if the
537 // revealed activity has a forced rotation set, then the controller will get
538 // updated with the new rotation of the display. However, at the same time,
539 // SystemUI will try to hide the menu by creating an animation to the normal
540 // bounds which are now stale. In such a case we defer the animation to the
541 // normal bounds until after the next onMovementBoundsChanged() call to get the
542 // bounds in the new orientation
Winson Chungef4dc812017-04-11 13:31:44 -0700543 try {
544 int displayRotation = mPinnedStackController.getDisplayRotation();
545 if (mDisplayRotation != displayRotation) {
546 mDeferResizeToNormalBoundsUntilRotation = displayRotation;
547 }
548 } catch (RemoteException e) {
549 Log.e(TAG, "Could not get display rotation from controller");
550 }
551 }
552
553 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
554 Rect normalBounds = new Rect(mNormalBounds);
555 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
556 mNormalMovementBounds, mMovementBounds, mIsMinimized,
557 false /* immediate */);
558 mSavedSnapFraction = -1f;
559 }
560 } else {
Winson Chungbb233762017-05-15 14:20:46 -0700561 // If resizing is not allowed, then the PiP should be frozen until the transition
562 // ends as well
563 setTouchEnabled(false);
Winson Chungef4dc812017-04-11 13:31:44 -0700564 mSavedSnapFraction = -1f;
Winson Chunga29eb982016-12-14 12:01:27 -0800565 }
Winson Chungfa7053782016-11-08 15:45:10 -0800566 }
Mady Mellor637cd482017-03-21 10:39:42 -0700567 mMenuState = menuState;
568 updateMovementBounds(menuState);
569 if (menuState != MENU_STATE_CLOSE) {
Chenjie Yu52cacc62017-12-08 18:11:45 -0800570 MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
Mady Mellor637cd482017-03-21 10:39:42 -0700571 }
Winson Chungfa7053782016-11-08 15:45:10 -0800572 }
573
574 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800575 * @return the motion helper.
Winson Chungfa7053782016-11-08 15:45:10 -0800576 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800577 public PipMotionHelper getMotionHelper() {
578 return mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700579 }
Winson Chungfa7053782016-11-08 15:45:10 -0800580
581 /**
Winson Chungfa7053782016-11-08 15:45:10 -0800582 * Gesture controlling normal movement of the PIP.
583 */
584 private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700585 // Whether the PiP was on the left side of the screen at the start of the gesture
586 private boolean mStartedOnLeft;
Winson Chung8ec0e162017-07-07 14:49:49 -0700587 private final Point mStartPosition = new Point();
588 private final PointF mDelta = new PointF();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800589
590 @Override
591 public void onDown(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800592 if (!touchState.isUserInteracting()) {
593 return;
594 }
595
Mady Mellor15b29c72017-06-07 14:53:58 -0700596 Rect bounds = mMotionHelper.getBounds();
Winson Chung8ec0e162017-07-07 14:49:49 -0700597 mDelta.set(0f, 0f);
598 mStartPosition.set(bounds.left, bounds.top);
Mady Mellor15b29c72017-06-07 14:53:58 -0700599 mStartedOnLeft = bounds.left < mMovementBounds.centerX();
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700600 mMovementWithinMinimize = true;
601 mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
602
Mady Mellora7f69742017-02-03 11:00:20 -0800603 // If the menu is still visible, and we aren't minimized, then just poke the menu
604 // so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700605 if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
Mady Mellora7f69742017-02-03 11:00:20 -0800606 mMenuController.pokeMenu();
607 }
608
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900609 if (mEnableDimissDragToEdge) {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800610 mDismissViewController.createDismissTarget();
611 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
612 }
613 }
614
Winson Chungfa7053782016-11-08 15:45:10 -0800615 @Override
616 boolean onMove(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800617 if (!touchState.isUserInteracting()) {
618 return false;
619 }
620
Winson Chung2a82fe52017-02-02 14:43:34 -0800621 if (touchState.startedDragging()) {
622 mSavedSnapFraction = -1f;
Winson Chung2a82fe52017-02-02 14:43:34 -0800623
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900624 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700625 mHandler.removeCallbacks(mShowDismissAffordance);
626 mDismissViewController.showDismissTarget();
627 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800628 }
629
Winson Chungfa7053782016-11-08 15:45:10 -0800630 if (touchState.isDragging()) {
631 // Move the pinned stack freely
Winson Chung2a82fe52017-02-02 14:43:34 -0800632 final PointF lastDelta = touchState.getLastTouchDelta();
Winson Chung8ec0e162017-07-07 14:49:49 -0700633 float lastX = mStartPosition.x + mDelta.x;
634 float lastY = mStartPosition.y + mDelta.y;
635 float left = lastX + lastDelta.x;
636 float top = lastY + lastDelta.y;
Mady Mellor8c7dc422017-05-10 12:55:06 -0700637 if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800638 left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
Winson Chungfa7053782016-11-08 15:45:10 -0800639 }
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900640 if (mEnableDimissDragToEdge) {
Mady Mellor57d22552017-03-09 15:37:13 -0800641 // Allow pip to move past bottom bounds
642 top = Math.max(mMovementBounds.top, top);
643 } else {
644 top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
645 }
Winson Chung8ec0e162017-07-07 14:49:49 -0700646
647 // Add to the cumulative delta after bounding the position
648 mDelta.x += left - lastX;
649 mDelta.y += top - lastY;
650
651 mTmpBounds.set(mMotionHelper.getBounds());
Winson Chungfa7053782016-11-08 15:45:10 -0800652 mTmpBounds.offsetTo((int) left, (int) top);
Winson Chung2a82fe52017-02-02 14:43:34 -0800653 mMotionHelper.movePip(mTmpBounds);
654
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900655 if (mEnableDimissDragToEdge) {
Mady Mellor81d40612017-03-10 15:14:10 -0800656 updateDismissFraction();
657 }
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700658
659 final PointF curPos = touchState.getLastTouchPosition();
660 if (mMovementWithinMinimize) {
661 // Track if movement remains near starting edge to identify swipes to minimize
662 mMovementWithinMinimize = mStartedOnLeft
663 ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
664 : curPos.x >= mMovementBounds.right;
665 }
666 if (mMovementWithinDismiss) {
667 // Track if movement remains near the bottom edge to identify swipe to dismiss
668 mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
669 }
Winson Chungfa7053782016-11-08 15:45:10 -0800670 return true;
671 }
672 return false;
673 }
674
675 @Override
676 public boolean onUp(PipTouchState touchState) {
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900677 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700678 // Clean up the dismiss target regardless of the touch state in case the touch
679 // enabled state changes while the user is interacting
680 cleanUpDismissTarget();
681 }
682
Winson Chung85d39982017-02-24 15:21:25 -0800683 if (!touchState.isUserInteracting()) {
684 return false;
685 }
686
Mady Mellor60421c92017-03-29 15:27:37 -0700687 final PointF vel = touchState.getVelocity();
688 final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
689 final float velocity = PointF.length(vel.x, vel.y);
690 final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
Mady Mellor8c7dc422017-05-10 12:55:06 -0700691 final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
Mady Mellor47ba1402017-04-04 17:25:43 -0700692 && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
693 && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
694 vel.y, isFling);
695 final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
696 && (mMovementWithinDismiss || isUpWithinDimiss);
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900697 if (mEnableDimissDragToEdge) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700698 // Check if the user dragged or flung the PiP offscreen to dismiss it
699 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800700 MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
701 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chungb54b65b2017-04-26 14:02:13 -0700702 mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
703 vel.y, mUpdateScrimListener);
Winson Chungb54b65b2017-04-26 14:02:13 -0700704 return true;
Mady Mellord4e40fb2017-01-26 10:43:16 -0800705 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800706 }
Winson Chungd2d90972017-02-28 11:40:41 -0800707
Winson Chungfa7053782016-11-08 15:45:10 -0800708 if (touchState.isDragging()) {
Mady Mellor84a0f892017-03-27 14:10:46 -0700709 final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
710 && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700711 if (ENABLE_MINIMIZE &&
Mady Mellor2e138782017-03-27 11:09:50 -0700712 !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800713 // Pip should be minimized
Winson Chung2a82fe52017-02-02 14:43:34 -0800714 setMinimizedStateInternal(true);
Mady Mellor637cd482017-03-21 10:39:42 -0700715 if (mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800716 // If the user dragged the expanded PiP to the edge, then hiding the menu
717 // will trigger the PiP to be scaled back to the normal size with the
718 // minimize offset adjusted
719 mMenuController.hideMenu();
720 } else {
Mady Mellor81d40612017-03-10 15:14:10 -0800721 mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
722 mUpdateScrimListener);
Winson Chungd2d90972017-02-28 11:40:41 -0800723 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800724 return true;
725 }
726 if (mIsMinimized) {
Winson Chungd2d90972017-02-28 11:40:41 -0800727 // If we're dragging and it wasn't a minimize gesture then we shouldn't be
728 // minimized.
Winson Chung2a82fe52017-02-02 14:43:34 -0800729 setMinimizedStateInternal(false);
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800730 }
731
Winson Chung87e5d552017-04-05 11:49:38 -0700732 AnimatorListenerAdapter postAnimationCallback = null;
Mady Mellor637cd482017-03-21 10:39:42 -0700733 if (mMenuState != MENU_STATE_NONE) {
Winson Chung87e5d552017-04-05 11:49:38 -0700734 // If the menu is still visible, and we aren't minimized, then just poke the
735 // menu so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700736 mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700737 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Winson Chung87e5d552017-04-05 11:49:38 -0700738 } else {
739 // If the menu is not visible, then we can still be showing the activity for the
740 // dismiss overlay, so just finish it after the animation completes
741 postAnimationCallback = new AnimatorListenerAdapter() {
742 @Override
743 public void onAnimationEnd(Animator animation) {
744 mMenuController.hideMenu();
745 }
746 };
Winson Chungd2d90972017-02-28 11:40:41 -0800747 }
748
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700749 if (isFling) {
Mady Mellor81d40612017-03-10 15:14:10 -0800750 mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
Mady Mellor15b29c72017-06-07 14:53:58 -0700751 mUpdateScrimListener, postAnimationCallback,
752 mStartPosition);
Winson Chungfa7053782016-11-08 15:45:10 -0800753 } else {
Winson Chung87e5d552017-04-05 11:49:38 -0700754 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
755 postAnimationCallback);
Winson Chungfa7053782016-11-08 15:45:10 -0800756 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800757 } else if (mIsMinimized) {
758 // This was a tap, so no longer minimized
Winson Chung87e5d552017-04-05 11:49:38 -0700759 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */,
760 null /* animatorListener */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800761 setMinimizedStateInternal(false);
Arthur Hung0ede6e92019-08-30 16:06:59 +0800762 } else if (mTouchState.isDoubleTap()) {
763 // Expand to fullscreen if this is a double tap
764 mMotionHelper.expandPip();
Mady Mellor637cd482017-03-21 10:39:42 -0700765 } else if (mMenuState != MENU_STATE_FULL) {
Arthur Hung0ede6e92019-08-30 16:06:59 +0800766 if (!mTouchState.isWaitingForDoubleTap()) {
Winson Chungbca03112017-08-16 10:38:15 -0700767 // User has stalled long enough for this not to be a drag or a double tap, just
768 // expand the menu
769 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
770 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
771 } else {
772 // Next touch event _may_ be the second tap for the double-tap, schedule a
773 // fallback runnable to trigger the menu if no touch event occurs before the
774 // next tap
775 mTouchState.scheduleDoubleTapTimeoutCallback();
776 }
Winson Chungfa7053782016-11-08 15:45:10 -0800777 }
778 return true;
779 }
780 };
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800781
782 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800783 * Updates the current movement bounds based on whether the menu is currently visible.
784 */
Mady Mellor637cd482017-03-21 10:39:42 -0700785 private void updateMovementBounds(int menuState) {
Winson Chunga71febe2017-05-22 11:14:22 -0700786 boolean isMenuExpanded = menuState == MENU_STATE_FULL;
787 mMovementBounds = isMenuExpanded
Winson Chung2a82fe52017-02-02 14:43:34 -0800788 ? mExpandedMovementBounds
789 : mNormalMovementBounds;
Winson Chunga71febe2017-05-22 11:14:22 -0700790 try {
Winson Chung54844b42019-04-03 11:32:41 -0700791 if (mPinnedStackController != null) {
792 mPinnedStackController.setMinEdgeSize(
793 isMenuExpanded ? mExpandedShortestEdgeSize : 0);
794 }
Winson Chunga71febe2017-05-22 11:14:22 -0700795 } catch (RemoteException e) {
796 Log.e(TAG, "Could not set minimized state", e);
797 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800798 }
Winson Chung29a78652017-02-09 18:35:26 -0800799
Winson Chungb54b65b2017-04-26 14:02:13 -0700800 /**
801 * Removes the dismiss target and cancels any pending callbacks to show it.
802 */
803 private void cleanUpDismissTarget() {
804 mHandler.removeCallbacks(mShowDismissAffordance);
805 mDismissViewController.destroyDismissTarget();
806 }
807
Winson Chung9b919412017-06-19 17:01:51 -0700808 /**
809 * Resets some states related to the touch handling.
810 */
811 private void cleanUp() {
812 if (mIsMinimized) {
813 setMinimizedStateInternal(false);
814 }
815 cleanUpDismissTarget();
816 }
817
Winson Chungbb787442017-09-01 11:33:47 -0700818 /**
819 * @return whether the menu will resize as a part of showing the full menu.
820 */
821 private boolean willResizeMenu() {
822 return mExpandedBounds.width() != mNormalBounds.width() ||
823 mExpandedBounds.height() != mNormalBounds.height();
824 }
825
Winson Chung29a78652017-02-09 18:35:26 -0800826 public void dump(PrintWriter pw, String prefix) {
827 final String innerPrefix = prefix + " ";
828 pw.println(prefix + TAG);
829 pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
830 pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
831 pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
832 pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
833 pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700834 pw.println(innerPrefix + "mMenuState=" + mMenuState);
Winson Chungd2d90972017-02-28 11:40:41 -0800835 pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
Winson Chung29a78652017-02-09 18:35:26 -0800836 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
837 pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
Tracy Zhou43513082018-03-08 21:58:36 -0800838 pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
839 pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
Winson Chung29a78652017-02-09 18:35:26 -0800840 pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
Eliot Courtney3dc12f12018-06-01 14:26:26 +0900841 pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDimissDragToEdge);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700842 pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
Winson Chung29a78652017-02-09 18:35:26 -0800843 mSnapAlgorithm.dump(pw, innerPrefix);
844 mTouchState.dump(pw, innerPrefix);
845 mMotionHelper.dump(pw, innerPrefix);
846 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800847
Winson73bc1592016-10-18 18:47:43 -0700848}