blob: 51175d1d7cf020ab307c46ee151856bd215f2986 [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_NONE;
20import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
21import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
22
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;
Winson Chung9b919412017-06-19 17:01:51 -070028import android.content.ComponentName;
Winson73bc1592016-10-18 18:47:43 -070029import android.content.Context;
Winson Chung136d1ec82017-07-18 11:16:30 -070030import android.content.res.Resources;
Mady Mellora7f69742017-02-03 11:00:20 -080031import android.graphics.Point;
Winson73bc1592016-10-18 18:47:43 -070032import android.graphics.PointF;
33import android.graphics.Rect;
Mady Mellord4e40fb2017-01-26 10:43:16 -080034import android.os.Handler;
Winson73bc1592016-10-18 18:47:43 -070035import android.os.RemoteException;
36import android.util.Log;
Mady Mellora7f69742017-02-03 11:00:20 -080037import android.util.Size;
Winson Chung655332c2016-10-31 13:14:28 -070038import android.view.IPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070039import android.view.MotionEvent;
Winson73bc1592016-10-18 18:47:43 -070040import android.view.ViewConfiguration;
Phil Weaverf00cd142017-03-03 13:44:00 -080041import android.view.accessibility.AccessibilityEvent;
42import android.view.accessibility.AccessibilityManager;
43import android.view.accessibility.AccessibilityNodeInfo;
Phil Weaver0a8caa12017-08-09 11:28:41 -070044import android.view.accessibility.AccessibilityWindowInfo;
Winson73bc1592016-10-18 18:47:43 -070045
Winson Chung14fbe142016-12-19 16:18:24 -080046import com.android.internal.logging.MetricsLogger;
47import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
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;
Winson73bc1592016-10-18 18:47:43 -070050import com.android.systemui.statusbar.FlingAnimationUtils;
51
Winson Chung29a78652017-02-09 18:35:26 -080052import java.io.PrintWriter;
53
Winson73bc1592016-10-18 18:47:43 -070054/**
55 * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
56 * the PIP.
57 */
Mady Mellor8c7dc422017-05-10 12:55:06 -070058public class PipTouchHandler {
Winson73bc1592016-10-18 18:47:43 -070059 private static final String TAG = "PipTouchHandler";
Winson73bc1592016-10-18 18:47:43 -070060
Mady Mellor8c7dc422017-05-10 12:55:06 -070061 // Allow the PIP to be dragged to the edge of the screen to be minimized.
62 private static final boolean ENABLE_MINIMIZE = false;
63 // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
64 private static final boolean ENABLE_FLING_DISMISS = false;
Mady Mellor2e138782017-03-27 11:09:50 -070065
Winson Chung14fbe142016-12-19 16:18:24 -080066 // These values are used for metrics and should never change
67 private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
68 private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
69
Mady Mellor5d58d252017-04-18 12:48:04 -070070 private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
Winson Chungfa7053782016-11-08 15:45:10 -080071
Winson Chung6e35ee1f2017-02-14 12:06:44 -080072 // Allow dragging the PIP to a location to close it
Mady Mellor81d40612017-03-10 15:14:10 -080073 private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
Winson Chung6e35ee1f2017-02-14 12:06:44 -080074
Winson73bc1592016-10-18 18:47:43 -070075 private final Context mContext;
76 private final IActivityManager mActivityManager;
77 private final ViewConfiguration mViewConfig;
Winson Chung15504af2016-11-02 18:11:36 -070078 private final PipMenuListener mMenuListener = new PipMenuListener();
Winson Chung655332c2016-10-31 13:14:28 -070079 private IPinnedStackController mPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070080
Winson Chung2a82fe52017-02-02 14:43:34 -080081 private final PipMenuActivityController mMenuController;
82 private final PipDismissViewController mDismissViewController;
Winson Chung14fefc22016-11-02 10:02:29 -070083 private final PipSnapAlgorithm mSnapAlgorithm;
Phil Weaverf00cd142017-03-03 13:44:00 -080084 private final AccessibilityManager mAccessibilityManager;
Wale Ogunwale6455e502017-04-17 14:16:43 -070085 private boolean mShowPipMenuOnAnimationEnd = false;
Winson73bc1592016-10-18 18:47:43 -070086
Winson Chung2a82fe52017-02-02 14:43:34 -080087 // The current movement bounds
88 private Rect mMovementBounds = new Rect();
89
Winson Chung0a2f34f2017-08-11 18:13:24 -070090 // The reference inset bounds, used to determine the dismiss fraction
91 private Rect mInsetBounds = new Rect();
Winson Chung2a82fe52017-02-02 14:43:34 -080092 // The reference bounds used to calculate the normal/expanded target bounds
93 private Rect mNormalBounds = new Rect();
94 private Rect mNormalMovementBounds = new Rect();
95 private Rect mExpandedBounds = new Rect();
96 private Rect mExpandedMovementBounds = new Rect();
Mady Mellora7f69742017-02-03 11:00:20 -080097 private int mExpandedShortestEdgeSize;
Winson73bc1592016-10-18 18:47:43 -070098
Winson Chungef4dc812017-04-11 13:31:44 -070099 // Used to workaround an issue where the WM rotation happens before we are notified, allowing
100 // us to send stale bounds
101 private int mDeferResizeToNormalBoundsUntilRotation = -1;
102 private int mDisplayRotation;
103
Mady Mellord4e40fb2017-01-26 10:43:16 -0800104 private Handler mHandler = new Handler();
105 private Runnable mShowDismissAffordance = new Runnable() {
106 @Override
107 public void run() {
Mady Mellor60421c92017-03-29 15:27:37 -0700108 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
109 mDismissViewController.showDismissTarget();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800110 }
111 }
112 };
Mady Mellor81d40612017-03-10 15:14:10 -0800113 private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
114 new AnimatorUpdateListener() {
115 @Override
116 public void onAnimationUpdate(ValueAnimator animation) {
117 updateDismissFraction();
118 }
119 };
Mady Mellord4e40fb2017-01-26 10:43:16 -0800120
Winson Chungfa7053782016-11-08 15:45:10 -0800121 // Behaviour states
Winson Chung9b919412017-06-19 17:01:51 -0700122 private int mMenuState = MENU_STATE_NONE;
Winson Chungd2d90972017-02-28 11:40:41 -0800123 private boolean mIsMinimized;
Winson Chung2a82fe52017-02-02 14:43:34 -0800124 private boolean mIsImeShowing;
125 private int mImeHeight;
Winson Chung136d1ec82017-07-18 11:16:30 -0700126 private int mImeOffset;
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() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800165 mMotionHelper.dismissPip();
Winson Chung14fbe142016-12-19 16:18:24 -0800166 MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
167 METRIC_VALUE_DISMISSED_BY_TAP);
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,
178 PipMenuActivityController menuController,
179 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;
Phil Weaverf00cd142017-03-03 13:44:00 -0800184 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Winson73bc1592016-10-18 18:47:43 -0700185 mViewConfig = ViewConfiguration.get(context);
Winson Chung15504af2016-11-02 18:11:36 -0700186 mMenuController = menuController;
187 mMenuController.addListener(mMenuListener);
188 mDismissViewController = new PipDismissViewController(context);
Winson Chung14fefc22016-11-02 10:02:29 -0700189 mSnapAlgorithm = new PipSnapAlgorithm(mContext);
Mady Mellor15b29c72017-06-07 14:53:58 -0700190 mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f);
Winson Chunga5acf182017-01-05 16:02:27 -0800191 mGestures = new PipTouchGesture[] {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800192 mDefaultMovementGesture
Winson Chungfa7053782016-11-08 15:45:10 -0800193 };
Winson Chung79f852e2017-05-04 15:06:18 -0700194 mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
195 mSnapAlgorithm, mFlingAnimationUtils);
Winson Chungbca03112017-08-16 10:38:15 -0700196 mTouchState = new PipTouchState(mViewConfig, mHandler,
197 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
198 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
Winson Chung136d1ec82017-07-18 11:16:30 -0700199
200 Resources res = context.getResources();
201 mExpandedShortestEdgeSize = res.getDimensionPixelSize(
Mady Mellora7f69742017-02-03 11:00:20 -0800202 R.dimen.pip_expanded_shortest_edge_size);
Winson Chung136d1ec82017-07-18 11:16:30 -0700203 mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
Winson Chungd2d90972017-02-28 11:40:41 -0800204
205 // Register the listener for input consumer touch events
206 inputConsumerController.setTouchListener(this::handleTouchEvent);
Phil Weaverf00cd142017-03-03 13:44:00 -0800207 inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
208 onRegistrationChanged(inputConsumerController.isRegistered());
Winson73bc1592016-10-18 18:47:43 -0700209 }
210
Winson Chung85d39982017-02-24 15:21:25 -0800211 public void setTouchEnabled(boolean enabled) {
212 mTouchState.setAllowTouches(enabled);
213 }
214
Winson Chungac52f282017-03-30 14:44:52 -0700215 public void showPictureInPictureMenu() {
216 // Only show the menu if the user isn't currently interacting with the PiP
217 if (!mTouchState.isUserInteracting()) {
Mady Mellor637cd482017-03-21 10:39:42 -0700218 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700219 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Winson Chungac52f282017-03-30 14:44:52 -0700220 }
221 }
222
Winson Chung929d4f72017-01-13 10:21:33 -0800223 public void onActivityPinned() {
Winson Chung9b919412017-06-19 17:01:51 -0700224 cleanUp();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700225 mShowPipMenuOnAnimationEnd = true;
Winson Chung929d4f72017-01-13 10:21:33 -0800226 }
227
Winson Chung9b919412017-06-19 17:01:51 -0700228 public void onActivityUnpinned(ComponentName topPipActivity) {
229 if (topPipActivity == null) {
230 // Clean up state after the last PiP activity is removed
231 cleanUp();
232 }
233 }
234
Winson Chungac52f282017-03-30 14:44:52 -0700235 public void onPinnedStackAnimationEnded() {
236 // Always synchronize the motion helper bounds once PiP animations finish
237 mMotionHelper.synchronizePinnedStackBounds();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700238
239 if (mShowPipMenuOnAnimationEnd) {
240 mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700241 mMovementBounds, true /* allowMenuTimeout */, false /* willResizeMenu */);
Wale Ogunwale6455e502017-04-17 14:16:43 -0700242 mShowPipMenuOnAnimationEnd = false;
243 }
Winson Chungac52f282017-03-30 14:44:52 -0700244 }
245
Winson Chung303c6b72016-10-24 17:12:49 -0700246 public void onConfigurationChanged() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800247 mMotionHelper.onConfigurationChanged();
248 mMotionHelper.synchronizePinnedStackBounds();
Winson Chung303c6b72016-10-24 17:12:49 -0700249 }
250
Winson Chung2a82fe52017-02-02 14:43:34 -0800251 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
252 mIsImeShowing = imeVisible;
253 mImeHeight = imeHeight;
254 }
255
Winson Chungbaa7b722017-03-03 21:33:44 -0800256 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
Winson Chungef4dc812017-04-11 13:31:44 -0700257 boolean fromImeAdjustement, int displayRotation) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800258 // Re-calculate the expanded bounds
259 mNormalBounds = normalBounds;
260 Rect normalMovementBounds = new Rect();
261 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
262 mIsImeShowing ? mImeHeight : 0);
Mady Mellora7f69742017-02-03 11:00:20 -0800263
264 // Calculate the expanded size
265 float aspectRatio = (float) normalBounds.width() / normalBounds.height();
266 Point displaySize = new Point();
267 mContext.getDisplay().getRealSize(displaySize);
268 Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
269 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
270 mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Winson Chung2a82fe52017-02-02 14:43:34 -0800271 Rect expandedMovementBounds = new Rect();
272 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
273 mIsImeShowing ? mImeHeight : 0);
274
Winson Chung2a82fe52017-02-02 14:43:34 -0800275 // If this is from an IME adjustment, then we should move the PiP so that it is not occluded
276 // by the IME
277 if (fromImeAdjustement) {
278 if (mTouchState.isUserInteracting()) {
279 // Defer the update of the current movement bounds until after the user finishes
280 // touching the screen
281 } else {
Winson Chungbaa7b722017-03-03 21:33:44 -0800282 final Rect bounds = new Rect(animatingBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700283 final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
Winson Chung2a82fe52017-02-02 14:43:34 -0800284 ? expandedMovementBounds
285 : normalMovementBounds;
286 if (mIsImeShowing) {
Winson Chung136d1ec82017-07-18 11:16:30 -0700287 // IME visible, apply the IME offset if the space allows for it
288 final int imeOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
289 toMovementBounds.bottom - mImeOffset);
Winson Chung2a82fe52017-02-02 14:43:34 -0800290 if (bounds.top == mMovementBounds.bottom) {
291 // If the PIP is currently resting on top of the IME, then adjust it with
Winson Chung136d1ec82017-07-18 11:16:30 -0700292 // the showing IME
293 bounds.offsetTo(bounds.left, toMovementBounds.bottom - imeOffset);
Winson Chung2a82fe52017-02-02 14:43:34 -0800294 } else {
Winson Chung136d1ec82017-07-18 11:16:30 -0700295 bounds.offset(0, Math.min(0, toMovementBounds.bottom - imeOffset
296 - bounds.top));
Winson Chung2a82fe52017-02-02 14:43:34 -0800297 }
298 } else {
299 // IME hidden
Winson Chung136d1ec82017-07-18 11:16:30 -0700300 if (bounds.top >= (mMovementBounds.bottom - mImeOffset)) {
301 // If the PIP is resting on top of the IME, then adjust it with the hiding
302 // IME
Winson Chung2a82fe52017-02-02 14:43:34 -0800303 bounds.offsetTo(bounds.left, toMovementBounds.bottom);
304 }
305 }
Winson Chungbaa7b722017-03-03 21:33:44 -0800306 mMotionHelper.animateToIMEOffset(bounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800307 }
Winson Chung14fbe142016-12-19 16:18:24 -0800308 }
Winson Chunga29eb982016-12-14 12:01:27 -0800309
Winson Chung2a82fe52017-02-02 14:43:34 -0800310 // Update the movement bounds after doing the calculations based on the old movement bounds
311 // above
312 mNormalMovementBounds = normalMovementBounds;
313 mExpandedMovementBounds = expandedMovementBounds;
Winson Chungef4dc812017-04-11 13:31:44 -0700314 mDisplayRotation = displayRotation;
Winson Chung0a2f34f2017-08-11 18:13:24 -0700315 mInsetBounds.set(insetBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700316 updateMovementBounds(mMenuState);
Winson Chungef4dc812017-04-11 13:31:44 -0700317
318 // If we have a deferred resize, apply it now
319 if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
320 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
321 mNormalMovementBounds, mMovementBounds, mIsMinimized,
322 true /* immediate */);
323 mSavedSnapFraction = -1f;
324 mDeferResizeToNormalBoundsUntilRotation = -1;
325 }
Winson Chunga29eb982016-12-14 12:01:27 -0800326 }
327
Phil Weaverf00cd142017-03-03 13:44:00 -0800328 private void onRegistrationChanged(boolean isRegistered) {
329 mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
Winson Chungfe1fa642017-03-13 10:51:22 -0700330 ? new PipAccessibilityInteractionConnection(mMotionHelper,
331 this::onAccessibilityShowMenu, mHandler) : null);
Winson Chungb54b65b2017-04-26 14:02:13 -0700332
333 if (!isRegistered && mTouchState.isUserInteracting()) {
334 // If the input consumer is unregistered while the user is interacting, then we may not
335 // get the final TOUCH_UP event, so clean up the dismiss target as well
336 cleanUpDismissTarget();
337 }
Winson Chungfe1fa642017-03-13 10:51:22 -0700338 }
339
340 private void onAccessibilityShowMenu() {
Mady Mellor637cd482017-03-21 10:39:42 -0700341 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700342 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Phil Weaverf00cd142017-03-03 13:44:00 -0800343 }
344
Winson Chung15504af2016-11-02 18:11:36 -0700345 private boolean handleTouchEvent(MotionEvent ev) {
Winson Chung655332c2016-10-31 13:14:28 -0700346 // Skip touch handling until we are bound to the controller
347 if (mPinnedStackController == null) {
Winson Chung15504af2016-11-02 18:11:36 -0700348 return true;
Winson Chung655332c2016-10-31 13:14:28 -0700349 }
350
Winson Chungfa7053782016-11-08 15:45:10 -0800351 // Update the touch state
352 mTouchState.onTouchEvent(ev);
353
Winson73bc1592016-10-18 18:47:43 -0700354 switch (ev.getAction()) {
355 case MotionEvent.ACTION_DOWN: {
Winson Chung2a82fe52017-02-02 14:43:34 -0800356 mMotionHelper.synchronizePinnedStackBounds();
Winson73bc1592016-10-18 18:47:43 -0700357
Winson Chungfa7053782016-11-08 15:45:10 -0800358 for (PipTouchGesture gesture : mGestures) {
359 gesture.onDown(mTouchState);
360 }
Winson73bc1592016-10-18 18:47:43 -0700361 break;
362 }
363 case MotionEvent.ACTION_MOVE: {
Winson Chungfa7053782016-11-08 15:45:10 -0800364 for (PipTouchGesture gesture : mGestures) {
365 if (gesture.onMove(mTouchState)) {
366 break;
Winson73bc1592016-10-18 18:47:43 -0700367 }
368 }
Winson73bc1592016-10-18 18:47:43 -0700369 break;
370 }
371 case MotionEvent.ACTION_UP: {
Winson Chung14fefc22016-11-02 10:02:29 -0700372 // Update the movement bounds again if the state has changed since the user started
373 // dragging (ie. when the IME shows)
Mady Mellor637cd482017-03-21 10:39:42 -0700374 updateMovementBounds(mMenuState);
Winson Chung14fefc22016-11-02 10:02:29 -0700375
Winson Chungfa7053782016-11-08 15:45:10 -0800376 for (PipTouchGesture gesture : mGestures) {
377 if (gesture.onUp(mTouchState)) {
378 break;
Winson Chung5cd26ff2016-10-24 11:50:44 -0700379 }
Winson Chung5cd26ff2016-10-24 11:50:44 -0700380 }
Winson73bc1592016-10-18 18:47:43 -0700381
382 // Fall through to clean up
383 }
384 case MotionEvent.ACTION_CANCEL: {
Winson Chung85d39982017-02-24 15:21:25 -0800385 mTouchState.reset();
Winson73bc1592016-10-18 18:47:43 -0700386 break;
387 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800388 case MotionEvent.ACTION_HOVER_ENTER:
389 case MotionEvent.ACTION_HOVER_MOVE: {
Svet Ganov8ddfb472017-11-15 20:15:25 -0800390 if (mAccessibilityManager.isObservedEventType(
391 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
392 && !mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800393 AccessibilityEvent event = AccessibilityEvent.obtain(
394 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700395 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700396 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700397 event.setWindowId(
398 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800399 mAccessibilityManager.sendAccessibilityEvent(event);
400 mSendingHoverAccessibilityEvents = true;
401 }
402 break;
403 }
404 case MotionEvent.ACTION_HOVER_EXIT: {
Svet Ganov8ddfb472017-11-15 20:15:25 -0800405 if (mAccessibilityManager.isObservedEventType(
406 AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
407 && mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800408 AccessibilityEvent event = AccessibilityEvent.obtain(
409 AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
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 = false;
416 }
417 break;
418 }
Winson73bc1592016-10-18 18:47:43 -0700419 }
Mady Mellor637cd482017-03-21 10:39:42 -0700420 return mMenuState == MENU_STATE_NONE;
Winson Chung15504af2016-11-02 18:11:36 -0700421 }
422
423 /**
Mady Mellor81d40612017-03-10 15:14:10 -0800424 * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
425 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700426 private void updateDismissFraction() {
Winson Chung0a2f34f2017-08-11 18:13:24 -0700427 // Skip updating the dismiss fraction when the IME is showing. This is to work around an
428 // issue where starting the menu activity for the dismiss overlay will steal the window
429 // focus, which closes the IME.
430 if (mMenuController != null && !mIsImeShowing) {
Mady Mellor81d40612017-03-10 15:14:10 -0800431 Rect bounds = mMotionHelper.getBounds();
Winson Chung0a2f34f2017-08-11 18:13:24 -0700432 final float target = mInsetBounds.bottom;
Mady Mellor81d40612017-03-10 15:14:10 -0800433 float fraction = 0f;
434 if (bounds.bottom > target) {
435 final float distance = bounds.bottom - target;
436 fraction = Math.min(distance / bounds.height(), 1f);
437 }
Winson Chungbca03112017-08-16 10:38:15 -0700438 if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
Winson Chung87e5d552017-04-05 11:49:38 -0700439 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
440 mMenuController.setDismissFraction(fraction);
441 }
Mady Mellor81d40612017-03-10 15:14:10 -0800442 }
443 }
444
445 /**
Winson Chunga29eb982016-12-14 12:01:27 -0800446 * Sets the controller to update the system of changes from user interaction.
447 */
448 void setPinnedStackController(IPinnedStackController controller) {
449 mPinnedStackController = controller;
450 }
451
452 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800453 * Sets the minimized state.
Winson Chungdff5c082016-11-02 17:28:03 -0700454 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700455 private void setMinimizedStateInternal(boolean isMinimized) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700456 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700457 return;
458 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800459 setMinimizedState(isMinimized, false /* fromController */);
Winson Chungdff5c082016-11-02 17:28:03 -0700460 }
461
462 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800463 * Sets the minimized state.
Winson Chungfa7053782016-11-08 15:45:10 -0800464 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800465 void setMinimizedState(boolean isMinimized, boolean fromController) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700466 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700467 return;
468 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800469 if (mIsMinimized != isMinimized) {
470 MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
471 isMinimized);
472 }
473 mIsMinimized = isMinimized;
474 mSnapAlgorithm.setMinimized(isMinimized);
Winson Chung54f0c652016-12-06 14:46:31 -0800475
Winson Chung2a82fe52017-02-02 14:43:34 -0800476 if (fromController) {
477 if (isMinimized) {
478 // Move the PiP to the new bounds immediately if minimized
479 mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
480 mMovementBounds));
481 }
482 } else if (mPinnedStackController != null) {
Winson Chung54f0c652016-12-06 14:46:31 -0800483 try {
484 mPinnedStackController.setIsMinimized(isMinimized);
485 } catch (RemoteException e) {
486 Log.e(TAG, "Could not set minimized state", e);
487 }
Winson Chungfa7053782016-11-08 15:45:10 -0800488 }
489 }
490
491 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800492 * Sets the menu visibility.
Winson Chungfa7053782016-11-08 15:45:10 -0800493 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700494 private void setMenuState(int menuState, boolean resize) {
Mady Mellor637cd482017-03-21 10:39:42 -0700495 if (menuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800496 // Save the current snap fraction and if we do not drag or move the PiP, then
497 // we store back to this snap fraction. Otherwise, we'll reset the snap
498 // fraction and snap to the closest edge
499 Rect expandedBounds = new Rect(mExpandedBounds);
500 if (resize) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800501 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
502 mMovementBounds, mExpandedMovementBounds);
Winson Chungd2d90972017-02-28 11:40:41 -0800503 }
Mady Mellor637cd482017-03-21 10:39:42 -0700504 } else if (menuState == MENU_STATE_NONE) {
Winson Chungd2d90972017-02-28 11:40:41 -0800505 // Try and restore the PiP to the closest edge, using the saved snap fraction
506 // if possible
507 if (resize) {
Winson Chungef4dc812017-04-11 13:31:44 -0700508 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Winson Chungbb233762017-05-15 14:20:46 -0700509 // This is a very special case: when the menu is expanded and visible,
510 // navigating to another activity can trigger auto-enter PiP, and if the
511 // revealed activity has a forced rotation set, then the controller will get
512 // updated with the new rotation of the display. However, at the same time,
513 // SystemUI will try to hide the menu by creating an animation to the normal
514 // bounds which are now stale. In such a case we defer the animation to the
515 // normal bounds until after the next onMovementBoundsChanged() call to get the
516 // bounds in the new orientation
Winson Chungef4dc812017-04-11 13:31:44 -0700517 try {
518 int displayRotation = mPinnedStackController.getDisplayRotation();
519 if (mDisplayRotation != displayRotation) {
520 mDeferResizeToNormalBoundsUntilRotation = displayRotation;
521 }
522 } catch (RemoteException e) {
523 Log.e(TAG, "Could not get display rotation from controller");
524 }
525 }
526
527 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
528 Rect normalBounds = new Rect(mNormalBounds);
529 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
530 mNormalMovementBounds, mMovementBounds, mIsMinimized,
531 false /* immediate */);
532 mSavedSnapFraction = -1f;
533 }
534 } else {
Winson Chungbb233762017-05-15 14:20:46 -0700535 // If resizing is not allowed, then the PiP should be frozen until the transition
536 // ends as well
537 setTouchEnabled(false);
Winson Chungef4dc812017-04-11 13:31:44 -0700538 mSavedSnapFraction = -1f;
Winson Chunga29eb982016-12-14 12:01:27 -0800539 }
Winson Chungfa7053782016-11-08 15:45:10 -0800540 }
Mady Mellor637cd482017-03-21 10:39:42 -0700541 mMenuState = menuState;
542 updateMovementBounds(menuState);
543 if (menuState != MENU_STATE_CLOSE) {
544 MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
545 menuState == MENU_STATE_FULL);
546 }
Winson Chungfa7053782016-11-08 15:45:10 -0800547 }
548
549 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800550 * @return the motion helper.
Winson Chungfa7053782016-11-08 15:45:10 -0800551 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800552 public PipMotionHelper getMotionHelper() {
553 return mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700554 }
Winson Chungfa7053782016-11-08 15:45:10 -0800555
556 /**
Winson Chungfa7053782016-11-08 15:45:10 -0800557 * Gesture controlling normal movement of the PIP.
558 */
559 private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700560 // Whether the PiP was on the left side of the screen at the start of the gesture
561 private boolean mStartedOnLeft;
Winson Chung8ec0e162017-07-07 14:49:49 -0700562 private final Point mStartPosition = new Point();
563 private final PointF mDelta = new PointF();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800564
565 @Override
566 public void onDown(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800567 if (!touchState.isUserInteracting()) {
568 return;
569 }
570
Mady Mellor15b29c72017-06-07 14:53:58 -0700571 Rect bounds = mMotionHelper.getBounds();
Winson Chung8ec0e162017-07-07 14:49:49 -0700572 mDelta.set(0f, 0f);
573 mStartPosition.set(bounds.left, bounds.top);
Mady Mellor15b29c72017-06-07 14:53:58 -0700574 mStartedOnLeft = bounds.left < mMovementBounds.centerX();
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700575 mMovementWithinMinimize = true;
576 mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
577
Mady Mellora7f69742017-02-03 11:00:20 -0800578 // If the menu is still visible, and we aren't minimized, then just poke the menu
579 // so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700580 if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
Mady Mellora7f69742017-02-03 11:00:20 -0800581 mMenuController.pokeMenu();
582 }
583
Mady Mellor60421c92017-03-29 15:27:37 -0700584 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800585 mDismissViewController.createDismissTarget();
586 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
587 }
588 }
589
Winson Chungfa7053782016-11-08 15:45:10 -0800590 @Override
591 boolean onMove(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800592 if (!touchState.isUserInteracting()) {
593 return false;
594 }
595
Winson Chung2a82fe52017-02-02 14:43:34 -0800596 if (touchState.startedDragging()) {
597 mSavedSnapFraction = -1f;
Winson Chung2a82fe52017-02-02 14:43:34 -0800598
Winson Chungb54b65b2017-04-26 14:02:13 -0700599 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
600 mHandler.removeCallbacks(mShowDismissAffordance);
601 mDismissViewController.showDismissTarget();
602 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800603 }
604
Winson Chungfa7053782016-11-08 15:45:10 -0800605 if (touchState.isDragging()) {
606 // Move the pinned stack freely
Winson Chung2a82fe52017-02-02 14:43:34 -0800607 final PointF lastDelta = touchState.getLastTouchDelta();
Winson Chung8ec0e162017-07-07 14:49:49 -0700608 float lastX = mStartPosition.x + mDelta.x;
609 float lastY = mStartPosition.y + mDelta.y;
610 float left = lastX + lastDelta.x;
611 float top = lastY + lastDelta.y;
Mady Mellor8c7dc422017-05-10 12:55:06 -0700612 if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800613 left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
Winson Chungfa7053782016-11-08 15:45:10 -0800614 }
Mady Mellor57d22552017-03-09 15:37:13 -0800615 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
616 // Allow pip to move past bottom bounds
617 top = Math.max(mMovementBounds.top, top);
618 } else {
619 top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
620 }
Winson Chung8ec0e162017-07-07 14:49:49 -0700621
622 // Add to the cumulative delta after bounding the position
623 mDelta.x += left - lastX;
624 mDelta.y += top - lastY;
625
626 mTmpBounds.set(mMotionHelper.getBounds());
Winson Chungfa7053782016-11-08 15:45:10 -0800627 mTmpBounds.offsetTo((int) left, (int) top);
Winson Chung2a82fe52017-02-02 14:43:34 -0800628 mMotionHelper.movePip(mTmpBounds);
629
Mady Mellor81d40612017-03-10 15:14:10 -0800630 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
631 updateDismissFraction();
632 }
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700633
634 final PointF curPos = touchState.getLastTouchPosition();
635 if (mMovementWithinMinimize) {
636 // Track if movement remains near starting edge to identify swipes to minimize
637 mMovementWithinMinimize = mStartedOnLeft
638 ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
639 : curPos.x >= mMovementBounds.right;
640 }
641 if (mMovementWithinDismiss) {
642 // Track if movement remains near the bottom edge to identify swipe to dismiss
643 mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
644 }
Winson Chungfa7053782016-11-08 15:45:10 -0800645 return true;
646 }
647 return false;
648 }
649
650 @Override
651 public boolean onUp(PipTouchState touchState) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700652 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
653 // Clean up the dismiss target regardless of the touch state in case the touch
654 // enabled state changes while the user is interacting
655 cleanUpDismissTarget();
656 }
657
Winson Chung85d39982017-02-24 15:21:25 -0800658 if (!touchState.isUserInteracting()) {
659 return false;
660 }
661
Mady Mellor60421c92017-03-29 15:27:37 -0700662 final PointF vel = touchState.getVelocity();
663 final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
664 final float velocity = PointF.length(vel.x, vel.y);
665 final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
Mady Mellor8c7dc422017-05-10 12:55:06 -0700666 final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
Mady Mellor47ba1402017-04-04 17:25:43 -0700667 && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
668 && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
669 vel.y, isFling);
670 final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
671 && (mMovementWithinDismiss || isUpWithinDimiss);
Mady Mellor60421c92017-03-29 15:27:37 -0700672 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700673 // Check if the user dragged or flung the PiP offscreen to dismiss it
674 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
675 mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
676 vel.y, mUpdateScrimListener);
677 MetricsLogger.action(mContext,
678 MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
679 METRIC_VALUE_DISMISSED_BY_DRAG);
680 return true;
Mady Mellord4e40fb2017-01-26 10:43:16 -0800681 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800682 }
Winson Chungd2d90972017-02-28 11:40:41 -0800683
Winson Chungfa7053782016-11-08 15:45:10 -0800684 if (touchState.isDragging()) {
Mady Mellor84a0f892017-03-27 14:10:46 -0700685 final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
686 && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700687 if (ENABLE_MINIMIZE &&
Mady Mellor2e138782017-03-27 11:09:50 -0700688 !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800689 // Pip should be minimized
Winson Chung2a82fe52017-02-02 14:43:34 -0800690 setMinimizedStateInternal(true);
Mady Mellor637cd482017-03-21 10:39:42 -0700691 if (mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800692 // If the user dragged the expanded PiP to the edge, then hiding the menu
693 // will trigger the PiP to be scaled back to the normal size with the
694 // minimize offset adjusted
695 mMenuController.hideMenu();
696 } else {
Mady Mellor81d40612017-03-10 15:14:10 -0800697 mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
698 mUpdateScrimListener);
Winson Chungd2d90972017-02-28 11:40:41 -0800699 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800700 return true;
701 }
702 if (mIsMinimized) {
Winson Chungd2d90972017-02-28 11:40:41 -0800703 // If we're dragging and it wasn't a minimize gesture then we shouldn't be
704 // minimized.
Winson Chung2a82fe52017-02-02 14:43:34 -0800705 setMinimizedStateInternal(false);
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800706 }
707
Winson Chung87e5d552017-04-05 11:49:38 -0700708 AnimatorListenerAdapter postAnimationCallback = null;
Mady Mellor637cd482017-03-21 10:39:42 -0700709 if (mMenuState != MENU_STATE_NONE) {
Winson Chung87e5d552017-04-05 11:49:38 -0700710 // If the menu is still visible, and we aren't minimized, then just poke the
711 // menu so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700712 mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700713 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Winson Chung87e5d552017-04-05 11:49:38 -0700714 } else {
715 // If the menu is not visible, then we can still be showing the activity for the
716 // dismiss overlay, so just finish it after the animation completes
717 postAnimationCallback = new AnimatorListenerAdapter() {
718 @Override
719 public void onAnimationEnd(Animator animation) {
720 mMenuController.hideMenu();
721 }
722 };
Winson Chungd2d90972017-02-28 11:40:41 -0800723 }
724
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700725 if (isFling) {
Mady Mellor81d40612017-03-10 15:14:10 -0800726 mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
Mady Mellor15b29c72017-06-07 14:53:58 -0700727 mUpdateScrimListener, postAnimationCallback,
728 mStartPosition);
Winson Chungfa7053782016-11-08 15:45:10 -0800729 } else {
Winson Chung87e5d552017-04-05 11:49:38 -0700730 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
731 postAnimationCallback);
Winson Chungfa7053782016-11-08 15:45:10 -0800732 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800733 } else if (mIsMinimized) {
734 // This was a tap, so no longer minimized
Winson Chung87e5d552017-04-05 11:49:38 -0700735 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */,
736 null /* animatorListener */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800737 setMinimizedStateInternal(false);
Mady Mellor637cd482017-03-21 10:39:42 -0700738 } else if (mMenuState != MENU_STATE_FULL) {
Winson Chungbca03112017-08-16 10:38:15 -0700739 if (mTouchState.isDoubleTap()) {
740 // Expand to fullscreen if this is a double tap
741 mMotionHelper.expandPip();
742 } else if (!mTouchState.isWaitingForDoubleTap()) {
743 // User has stalled long enough for this not to be a drag or a double tap, just
744 // expand the menu
745 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
746 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
747 } else {
748 // Next touch event _may_ be the second tap for the double-tap, schedule a
749 // fallback runnable to trigger the menu if no touch event occurs before the
750 // next tap
751 mTouchState.scheduleDoubleTapTimeoutCallback();
752 }
Winson Chungfa7053782016-11-08 15:45:10 -0800753 } else {
Winson Chungbe4a8082017-04-13 13:52:04 -0700754 mMenuController.hideMenu();
Winson Chung2a82fe52017-02-02 14:43:34 -0800755 mMotionHelper.expandPip();
Winson Chungfa7053782016-11-08 15:45:10 -0800756 }
757 return true;
758 }
759 };
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800760
761 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800762 * Updates the current movement bounds based on whether the menu is currently visible.
763 */
Mady Mellor637cd482017-03-21 10:39:42 -0700764 private void updateMovementBounds(int menuState) {
Winson Chunga71febe2017-05-22 11:14:22 -0700765 boolean isMenuExpanded = menuState == MENU_STATE_FULL;
766 mMovementBounds = isMenuExpanded
Winson Chung2a82fe52017-02-02 14:43:34 -0800767 ? mExpandedMovementBounds
768 : mNormalMovementBounds;
Winson Chunga71febe2017-05-22 11:14:22 -0700769 try {
770 mPinnedStackController.setMinEdgeSize(isMenuExpanded ? mExpandedShortestEdgeSize : 0);
771 } catch (RemoteException e) {
772 Log.e(TAG, "Could not set minimized state", e);
773 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800774 }
Winson Chung29a78652017-02-09 18:35:26 -0800775
Winson Chungb54b65b2017-04-26 14:02:13 -0700776 /**
777 * Removes the dismiss target and cancels any pending callbacks to show it.
778 */
779 private void cleanUpDismissTarget() {
780 mHandler.removeCallbacks(mShowDismissAffordance);
781 mDismissViewController.destroyDismissTarget();
782 }
783
Winson Chung9b919412017-06-19 17:01:51 -0700784 /**
785 * Resets some states related to the touch handling.
786 */
787 private void cleanUp() {
788 if (mIsMinimized) {
789 setMinimizedStateInternal(false);
790 }
791 cleanUpDismissTarget();
792 }
793
Winson Chungbb787442017-09-01 11:33:47 -0700794 /**
795 * @return whether the menu will resize as a part of showing the full menu.
796 */
797 private boolean willResizeMenu() {
798 return mExpandedBounds.width() != mNormalBounds.width() ||
799 mExpandedBounds.height() != mNormalBounds.height();
800 }
801
Winson Chung29a78652017-02-09 18:35:26 -0800802 public void dump(PrintWriter pw, String prefix) {
803 final String innerPrefix = prefix + " ";
804 pw.println(prefix + TAG);
805 pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
806 pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
807 pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
808 pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
809 pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700810 pw.println(innerPrefix + "mMenuState=" + mMenuState);
Winson Chungd2d90972017-02-28 11:40:41 -0800811 pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
Winson Chung29a78652017-02-09 18:35:26 -0800812 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
813 pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
814 pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
Mady Mellor60421c92017-03-29 15:27:37 -0700815 pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + ENABLE_DISMISS_DRAG_TO_EDGE);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700816 pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
Winson Chung29a78652017-02-09 18:35:26 -0800817 mSnapAlgorithm.dump(pw, innerPrefix);
818 mTouchState.dump(pw, innerPrefix);
819 mMotionHelper.dump(pw, innerPrefix);
820 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800821
Winson73bc1592016-10-18 18:47:43 -0700822}