blob: 5c0576d0de2fd313fd605091d6aca38bd216e678 [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;
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;
Chenjie Yuae9fdf042018-02-15 10:19:32 -080045
Chenjie Yu52cacc62017-12-08 18:11:45 -080046import com.android.internal.os.logging.MetricsLoggerWrapper;
Winson Chungcd1ff642016-10-26 09:44:43 -070047import com.android.internal.policy.PipSnapAlgorithm;
Mady Mellora7f69742017-02-03 11:00:20 -080048import com.android.systemui.R;
Winson Chungcbb15a92018-01-25 17:46:16 +000049import com.android.systemui.shared.system.InputConsumerController;
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
Mady Mellor5d58d252017-04-18 12:48:04 -070066 private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
Winson Chungfa7053782016-11-08 15:45:10 -080067
Winson Chung6e35ee1f2017-02-14 12:06:44 -080068 // Allow dragging the PIP to a location to close it
Mady Mellor81d40612017-03-10 15:14:10 -080069 private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
Winson Chung6e35ee1f2017-02-14 12:06:44 -080070
Winson73bc1592016-10-18 18:47:43 -070071 private final Context mContext;
72 private final IActivityManager mActivityManager;
73 private final ViewConfiguration mViewConfig;
Winson Chung15504af2016-11-02 18:11:36 -070074 private final PipMenuListener mMenuListener = new PipMenuListener();
Winson Chung655332c2016-10-31 13:14:28 -070075 private IPinnedStackController mPinnedStackController;
Winson73bc1592016-10-18 18:47:43 -070076
Winson Chung2a82fe52017-02-02 14:43:34 -080077 private final PipMenuActivityController mMenuController;
78 private final PipDismissViewController mDismissViewController;
Winson Chung14fefc22016-11-02 10:02:29 -070079 private final PipSnapAlgorithm mSnapAlgorithm;
Phil Weaverf00cd142017-03-03 13:44:00 -080080 private final AccessibilityManager mAccessibilityManager;
Wale Ogunwale6455e502017-04-17 14:16:43 -070081 private boolean mShowPipMenuOnAnimationEnd = false;
Winson73bc1592016-10-18 18:47:43 -070082
Winson Chung2a82fe52017-02-02 14:43:34 -080083 // The current movement bounds
84 private Rect mMovementBounds = new Rect();
85
Winson Chung0a2f34f2017-08-11 18:13:24 -070086 // The reference inset bounds, used to determine the dismiss fraction
87 private Rect mInsetBounds = new Rect();
Winson Chung2a82fe52017-02-02 14:43:34 -080088 // The reference bounds used to calculate the normal/expanded target bounds
89 private Rect mNormalBounds = new Rect();
90 private Rect mNormalMovementBounds = new Rect();
91 private Rect mExpandedBounds = new Rect();
92 private Rect mExpandedMovementBounds = new Rect();
Mady Mellora7f69742017-02-03 11:00:20 -080093 private int mExpandedShortestEdgeSize;
Winson73bc1592016-10-18 18:47:43 -070094
Winson Chungef4dc812017-04-11 13:31:44 -070095 // Used to workaround an issue where the WM rotation happens before we are notified, allowing
96 // us to send stale bounds
97 private int mDeferResizeToNormalBoundsUntilRotation = -1;
98 private int mDisplayRotation;
99
Mady Mellord4e40fb2017-01-26 10:43:16 -0800100 private Handler mHandler = new Handler();
101 private Runnable mShowDismissAffordance = new Runnable() {
102 @Override
103 public void run() {
Mady Mellor60421c92017-03-29 15:27:37 -0700104 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
105 mDismissViewController.showDismissTarget();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800106 }
107 }
108 };
Mady Mellor81d40612017-03-10 15:14:10 -0800109 private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
110 new AnimatorUpdateListener() {
111 @Override
112 public void onAnimationUpdate(ValueAnimator animation) {
113 updateDismissFraction();
114 }
115 };
Mady Mellord4e40fb2017-01-26 10:43:16 -0800116
Winson Chungfa7053782016-11-08 15:45:10 -0800117 // Behaviour states
Winson Chung9b919412017-06-19 17:01:51 -0700118 private int mMenuState = MENU_STATE_NONE;
Winson Chungd2d90972017-02-28 11:40:41 -0800119 private boolean mIsMinimized;
Winson Chung2a82fe52017-02-02 14:43:34 -0800120 private boolean mIsImeShowing;
121 private int mImeHeight;
Winson Chung136d1ec82017-07-18 11:16:30 -0700122 private int mImeOffset;
Tracy Zhou43513082018-03-08 21:58:36 -0800123 private boolean mIsShelfShowing;
124 private int mShelfHeight;
Winson Chung2a82fe52017-02-02 14:43:34 -0800125 private float mSavedSnapFraction = -1f;
Phil Weaverf00cd142017-03-03 13:44:00 -0800126 private boolean mSendingHoverAccessibilityEvents;
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700127 private boolean mMovementWithinMinimize;
128 private boolean mMovementWithinDismiss;
Winson73bc1592016-10-18 18:47:43 -0700129
Winson Chungfa7053782016-11-08 15:45:10 -0800130 // Touch state
131 private final PipTouchState mTouchState;
Winson73bc1592016-10-18 18:47:43 -0700132 private final FlingAnimationUtils mFlingAnimationUtils;
Winson Chungfa7053782016-11-08 15:45:10 -0800133 private final PipTouchGesture[] mGestures;
Winson Chung2a82fe52017-02-02 14:43:34 -0800134 private final PipMotionHelper mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700135
Winson Chung2a82fe52017-02-02 14:43:34 -0800136 // Temp vars
Winson Chung655332c2016-10-31 13:14:28 -0700137 private final Rect mTmpBounds = new Rect();
138
Winson73bc1592016-10-18 18:47:43 -0700139 /**
Winson Chung15504af2016-11-02 18:11:36 -0700140 * A listener for the PIP menu activity.
141 */
142 private class PipMenuListener implements PipMenuActivityController.Listener {
143 @Override
Mady Mellor637cd482017-03-21 10:39:42 -0700144 public void onPipMenuStateChanged(int menuState, boolean resize) {
145 setMenuState(menuState, resize);
Winson Chung15504af2016-11-02 18:11:36 -0700146 }
Winson Chunga29eb982016-12-14 12:01:27 -0800147
148 @Override
149 public void onPipExpand() {
150 if (!mIsMinimized) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800151 mMotionHelper.expandPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800152 }
153 }
154
155 @Override
156 public void onPipMinimize() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800157 setMinimizedStateInternal(true);
Mady Mellor81d40612017-03-10 15:14:10 -0800158 mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */);
Winson Chunga29eb982016-12-14 12:01:27 -0800159 }
160
161 @Override
162 public void onPipDismiss() {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800163 MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext,
164 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800165 mMotionHelper.dismissPip();
Winson Chunga29eb982016-12-14 12:01:27 -0800166 }
Mady Mellor637cd482017-03-21 10:39:42 -0700167
168 @Override
169 public void onPipShowMenu() {
170 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700171 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Mady Mellor637cd482017-03-21 10:39:42 -0700172 }
Winson Chung15504af2016-11-02 18:11:36 -0700173 }
174
Winson Chungd2d90972017-02-28 11:40:41 -0800175 public PipTouchHandler(Context context, IActivityManager activityManager,
176 PipMenuActivityController menuController,
177 InputConsumerController inputConsumerController) {
Winson73bc1592016-10-18 18:47:43 -0700178
179 // Initialize the Pip input consumer
Winson73bc1592016-10-18 18:47:43 -0700180 mContext = context;
181 mActivityManager = activityManager;
Phil Weaverf00cd142017-03-03 13:44:00 -0800182 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Winson73bc1592016-10-18 18:47:43 -0700183 mViewConfig = ViewConfiguration.get(context);
Winson Chung15504af2016-11-02 18:11:36 -0700184 mMenuController = menuController;
185 mMenuController.addListener(mMenuListener);
186 mDismissViewController = new PipDismissViewController(context);
Winson Chung14fefc22016-11-02 10:02:29 -0700187 mSnapAlgorithm = new PipSnapAlgorithm(mContext);
Mady Mellor15b29c72017-06-07 14:53:58 -0700188 mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f);
Winson Chunga5acf182017-01-05 16:02:27 -0800189 mGestures = new PipTouchGesture[] {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800190 mDefaultMovementGesture
Winson Chungfa7053782016-11-08 15:45:10 -0800191 };
Winson Chung79f852e2017-05-04 15:06:18 -0700192 mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController,
193 mSnapAlgorithm, mFlingAnimationUtils);
Winson Chungbca03112017-08-16 10:38:15 -0700194 mTouchState = new PipTouchState(mViewConfig, mHandler,
195 () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
196 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
Winson Chung136d1ec82017-07-18 11:16:30 -0700197
198 Resources res = context.getResources();
199 mExpandedShortestEdgeSize = res.getDimensionPixelSize(
Mady Mellora7f69742017-02-03 11:00:20 -0800200 R.dimen.pip_expanded_shortest_edge_size);
Winson Chung136d1ec82017-07-18 11:16:30 -0700201 mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
Winson Chungd2d90972017-02-28 11:40:41 -0800202
203 // Register the listener for input consumer touch events
204 inputConsumerController.setTouchListener(this::handleTouchEvent);
Phil Weaverf00cd142017-03-03 13:44:00 -0800205 inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
206 onRegistrationChanged(inputConsumerController.isRegistered());
Winson73bc1592016-10-18 18:47:43 -0700207 }
208
Winson Chung85d39982017-02-24 15:21:25 -0800209 public void setTouchEnabled(boolean enabled) {
210 mTouchState.setAllowTouches(enabled);
211 }
212
Winson Chungac52f282017-03-30 14:44:52 -0700213 public void showPictureInPictureMenu() {
214 // Only show the menu if the user isn't currently interacting with the PiP
215 if (!mTouchState.isUserInteracting()) {
Mady Mellor637cd482017-03-21 10:39:42 -0700216 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700217 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Winson Chungac52f282017-03-30 14:44:52 -0700218 }
219 }
220
Winson Chung929d4f72017-01-13 10:21:33 -0800221 public void onActivityPinned() {
Winson Chung9b919412017-06-19 17:01:51 -0700222 cleanUp();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700223 mShowPipMenuOnAnimationEnd = true;
Winson Chung929d4f72017-01-13 10:21:33 -0800224 }
225
Winson Chung9b919412017-06-19 17:01:51 -0700226 public void onActivityUnpinned(ComponentName topPipActivity) {
227 if (topPipActivity == null) {
228 // Clean up state after the last PiP activity is removed
229 cleanUp();
230 }
231 }
232
Winson Chungac52f282017-03-30 14:44:52 -0700233 public void onPinnedStackAnimationEnded() {
234 // Always synchronize the motion helper bounds once PiP animations finish
235 mMotionHelper.synchronizePinnedStackBounds();
Wale Ogunwale6455e502017-04-17 14:16:43 -0700236
237 if (mShowPipMenuOnAnimationEnd) {
238 mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700239 mMovementBounds, true /* allowMenuTimeout */, false /* willResizeMenu */);
Wale Ogunwale6455e502017-04-17 14:16:43 -0700240 mShowPipMenuOnAnimationEnd = false;
241 }
Winson Chungac52f282017-03-30 14:44:52 -0700242 }
243
Winson Chung303c6b72016-10-24 17:12:49 -0700244 public void onConfigurationChanged() {
Winson Chung2a82fe52017-02-02 14:43:34 -0800245 mMotionHelper.onConfigurationChanged();
246 mMotionHelper.synchronizePinnedStackBounds();
Winson Chung303c6b72016-10-24 17:12:49 -0700247 }
248
Winson Chung2a82fe52017-02-02 14:43:34 -0800249 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
250 mIsImeShowing = imeVisible;
251 mImeHeight = imeHeight;
252 }
253
Tracy Zhou43513082018-03-08 21:58:36 -0800254 public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
255 mIsShelfShowing = shelfVisible;
256 mShelfHeight = shelfHeight;
257 }
258
Winson Chungbaa7b722017-03-03 21:33:44 -0800259 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800260 boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) {
261 final int bottomOffset = mIsImeShowing ? mImeHeight : 0;
262
Winson Chung2a82fe52017-02-02 14:43:34 -0800263 // Re-calculate the expanded bounds
264 mNormalBounds = normalBounds;
265 Rect normalMovementBounds = new Rect();
266 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800267 bottomOffset);
Mady Mellora7f69742017-02-03 11:00:20 -0800268
269 // Calculate the expanded size
270 float aspectRatio = (float) normalBounds.width() / normalBounds.height();
271 Point displaySize = new Point();
272 mContext.getDisplay().getRealSize(displaySize);
273 Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
274 mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
275 mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
Winson Chung2a82fe52017-02-02 14:43:34 -0800276 Rect expandedMovementBounds = new Rect();
277 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
Tracy Zhou43513082018-03-08 21:58:36 -0800278 bottomOffset);
Winson Chung2a82fe52017-02-02 14:43:34 -0800279
Tracy Zhou43513082018-03-08 21:58:36 -0800280 // If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
281 // occluded by the IME or shelf.
282 if (fromImeAdjustment || fromShelfAdjustment) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800283 if (mTouchState.isUserInteracting()) {
284 // Defer the update of the current movement bounds until after the user finishes
285 // touching the screen
286 } else {
Tracy Zhou4be72d22018-03-22 11:41:00 -0700287 final int adjustedOffset = Math.max(mIsImeShowing ? mImeHeight + mImeOffset : 0,
288 mIsShelfShowing ? mShelfHeight : 0);
289 Rect normalAdjustedBounds = new Rect();
290 mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalAdjustedBounds,
291 adjustedOffset);
292 Rect expandedAdjustedBounds = new Rect();
293 mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds,
294 expandedAdjustedBounds, adjustedOffset);
295 final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
296 ? expandedAdjustedBounds
297 : normalAdjustedBounds;
298
299 animateToOffset(animatingBounds, toAdjustedBounds);
Winson Chung2a82fe52017-02-02 14:43:34 -0800300 }
Winson Chung14fbe142016-12-19 16:18:24 -0800301 }
Winson Chunga29eb982016-12-14 12:01:27 -0800302
Winson Chung2a82fe52017-02-02 14:43:34 -0800303 // Update the movement bounds after doing the calculations based on the old movement bounds
304 // above
305 mNormalMovementBounds = normalMovementBounds;
306 mExpandedMovementBounds = expandedMovementBounds;
Winson Chungef4dc812017-04-11 13:31:44 -0700307 mDisplayRotation = displayRotation;
Winson Chung0a2f34f2017-08-11 18:13:24 -0700308 mInsetBounds.set(insetBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700309 updateMovementBounds(mMenuState);
Winson Chungef4dc812017-04-11 13:31:44 -0700310
311 // If we have a deferred resize, apply it now
312 if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
313 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
314 mNormalMovementBounds, mMovementBounds, mIsMinimized,
315 true /* immediate */);
316 mSavedSnapFraction = -1f;
317 mDeferResizeToNormalBoundsUntilRotation = -1;
318 }
Winson Chunga29eb982016-12-14 12:01:27 -0800319 }
320
Tracy Zhou4be72d22018-03-22 11:41:00 -0700321 private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
Tracy Zhou43513082018-03-08 21:58:36 -0800322 final Rect bounds = new Rect(animatingBounds);
Tracy Zhou4be72d22018-03-22 11:41:00 -0700323 if (toAdjustedBounds.bottom < mMovementBounds.bottom
324 && bounds.top < toAdjustedBounds.bottom) {
325 return;
Tracy Zhou43513082018-03-08 21:58:36 -0800326 }
Tracy Zhou4be72d22018-03-22 11:41:00 -0700327 bounds.offset(0, toAdjustedBounds.bottom - bounds.top);
Tracy Zhou43513082018-03-08 21:58:36 -0800328 mMotionHelper.animateToOffset(bounds);
329 }
330
Phil Weaverf00cd142017-03-03 13:44:00 -0800331 private void onRegistrationChanged(boolean isRegistered) {
332 mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
Winson Chungfe1fa642017-03-13 10:51:22 -0700333 ? new PipAccessibilityInteractionConnection(mMotionHelper,
334 this::onAccessibilityShowMenu, mHandler) : null);
Winson Chungb54b65b2017-04-26 14:02:13 -0700335
336 if (!isRegistered && mTouchState.isUserInteracting()) {
337 // If the input consumer is unregistered while the user is interacting, then we may not
338 // get the final TOUCH_UP event, so clean up the dismiss target as well
339 cleanUpDismissTarget();
340 }
Winson Chungfe1fa642017-03-13 10:51:22 -0700341 }
342
343 private void onAccessibilityShowMenu() {
Mady Mellor637cd482017-03-21 10:39:42 -0700344 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700345 mMovementBounds, false /* allowMenuTimeout */, willResizeMenu());
Phil Weaverf00cd142017-03-03 13:44:00 -0800346 }
347
Winson Chung15504af2016-11-02 18:11:36 -0700348 private boolean handleTouchEvent(MotionEvent ev) {
Winson Chung655332c2016-10-31 13:14:28 -0700349 // Skip touch handling until we are bound to the controller
350 if (mPinnedStackController == null) {
Winson Chung15504af2016-11-02 18:11:36 -0700351 return true;
Winson Chung655332c2016-10-31 13:14:28 -0700352 }
353
Winson Chungfa7053782016-11-08 15:45:10 -0800354 // Update the touch state
355 mTouchState.onTouchEvent(ev);
356
Winson73bc1592016-10-18 18:47:43 -0700357 switch (ev.getAction()) {
358 case MotionEvent.ACTION_DOWN: {
Winson Chung2a82fe52017-02-02 14:43:34 -0800359 mMotionHelper.synchronizePinnedStackBounds();
Winson73bc1592016-10-18 18:47:43 -0700360
Winson Chungfa7053782016-11-08 15:45:10 -0800361 for (PipTouchGesture gesture : mGestures) {
362 gesture.onDown(mTouchState);
363 }
Winson73bc1592016-10-18 18:47:43 -0700364 break;
365 }
366 case MotionEvent.ACTION_MOVE: {
Winson Chungfa7053782016-11-08 15:45:10 -0800367 for (PipTouchGesture gesture : mGestures) {
368 if (gesture.onMove(mTouchState)) {
369 break;
Winson73bc1592016-10-18 18:47:43 -0700370 }
371 }
Winson73bc1592016-10-18 18:47:43 -0700372 break;
373 }
374 case MotionEvent.ACTION_UP: {
Winson Chung14fefc22016-11-02 10:02:29 -0700375 // Update the movement bounds again if the state has changed since the user started
376 // dragging (ie. when the IME shows)
Mady Mellor637cd482017-03-21 10:39:42 -0700377 updateMovementBounds(mMenuState);
Winson Chung14fefc22016-11-02 10:02:29 -0700378
Winson Chungfa7053782016-11-08 15:45:10 -0800379 for (PipTouchGesture gesture : mGestures) {
380 if (gesture.onUp(mTouchState)) {
381 break;
Winson Chung5cd26ff2016-10-24 11:50:44 -0700382 }
Winson Chung5cd26ff2016-10-24 11:50:44 -0700383 }
Winson73bc1592016-10-18 18:47:43 -0700384
385 // Fall through to clean up
386 }
387 case MotionEvent.ACTION_CANCEL: {
Winson Chung85d39982017-02-24 15:21:25 -0800388 mTouchState.reset();
Winson73bc1592016-10-18 18:47:43 -0700389 break;
390 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800391 case MotionEvent.ACTION_HOVER_ENTER:
392 case MotionEvent.ACTION_HOVER_MOVE: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000393 if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800394 AccessibilityEvent event = AccessibilityEvent.obtain(
395 AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700396 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700397 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700398 event.setWindowId(
399 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800400 mAccessibilityManager.sendAccessibilityEvent(event);
401 mSendingHoverAccessibilityEvents = true;
402 }
403 break;
404 }
405 case MotionEvent.ACTION_HOVER_EXIT: {
Eugene Suslad4128ec2017-12-04 19:48:41 +0000406 if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
Phil Weaverf00cd142017-03-03 13:44:00 -0800407 AccessibilityEvent event = AccessibilityEvent.obtain(
408 AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700409 event.setImportantForAccessibility(true);
Phil Weaver651fe9f2017-05-24 16:43:46 -0700410 event.setSourceNodeId(AccessibilityNodeInfo.ROOT_NODE_ID);
Phil Weaver0a8caa12017-08-09 11:28:41 -0700411 event.setWindowId(
412 AccessibilityWindowInfo.PICTURE_IN_PICTURE_ACTION_REPLACER_WINDOW_ID);
Phil Weaverf00cd142017-03-03 13:44:00 -0800413 mAccessibilityManager.sendAccessibilityEvent(event);
414 mSendingHoverAccessibilityEvents = false;
415 }
416 break;
417 }
Winson73bc1592016-10-18 18:47:43 -0700418 }
Mady Mellor637cd482017-03-21 10:39:42 -0700419 return mMenuState == MENU_STATE_NONE;
Winson Chung15504af2016-11-02 18:11:36 -0700420 }
421
422 /**
Mady Mellor81d40612017-03-10 15:14:10 -0800423 * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
424 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700425 private void updateDismissFraction() {
Winson Chung0a2f34f2017-08-11 18:13:24 -0700426 // Skip updating the dismiss fraction when the IME is showing. This is to work around an
427 // issue where starting the menu activity for the dismiss overlay will steal the window
428 // focus, which closes the IME.
429 if (mMenuController != null && !mIsImeShowing) {
Mady Mellor81d40612017-03-10 15:14:10 -0800430 Rect bounds = mMotionHelper.getBounds();
Winson Chung0a2f34f2017-08-11 18:13:24 -0700431 final float target = mInsetBounds.bottom;
Mady Mellor81d40612017-03-10 15:14:10 -0800432 float fraction = 0f;
433 if (bounds.bottom > target) {
434 final float distance = bounds.bottom - target;
435 fraction = Math.min(distance / bounds.height(), 1f);
436 }
Winson Chungbca03112017-08-16 10:38:15 -0700437 if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
Winson Chung87e5d552017-04-05 11:49:38 -0700438 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
439 mMenuController.setDismissFraction(fraction);
440 }
Mady Mellor81d40612017-03-10 15:14:10 -0800441 }
442 }
443
444 /**
Winson Chunga29eb982016-12-14 12:01:27 -0800445 * Sets the controller to update the system of changes from user interaction.
446 */
447 void setPinnedStackController(IPinnedStackController controller) {
448 mPinnedStackController = controller;
449 }
450
451 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800452 * Sets the minimized state.
Winson Chungdff5c082016-11-02 17:28:03 -0700453 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700454 private void setMinimizedStateInternal(boolean isMinimized) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700455 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700456 return;
457 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800458 setMinimizedState(isMinimized, false /* fromController */);
Winson Chungdff5c082016-11-02 17:28:03 -0700459 }
460
461 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800462 * Sets the minimized state.
Winson Chungfa7053782016-11-08 15:45:10 -0800463 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800464 void setMinimizedState(boolean isMinimized, boolean fromController) {
Mady Mellor8c7dc422017-05-10 12:55:06 -0700465 if (!ENABLE_MINIMIZE) {
Mady Mellor2e138782017-03-27 11:09:50 -0700466 return;
467 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800468 if (mIsMinimized != isMinimized) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800469 MetricsLoggerWrapper.logPictureInPictureMinimize(mContext,
470 isMinimized, PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chung2a82fe52017-02-02 14:43:34 -0800471 }
472 mIsMinimized = isMinimized;
473 mSnapAlgorithm.setMinimized(isMinimized);
Winson Chung54f0c652016-12-06 14:46:31 -0800474
Winson Chung2a82fe52017-02-02 14:43:34 -0800475 if (fromController) {
476 if (isMinimized) {
477 // Move the PiP to the new bounds immediately if minimized
478 mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
479 mMovementBounds));
480 }
481 } else if (mPinnedStackController != null) {
Winson Chung54f0c652016-12-06 14:46:31 -0800482 try {
483 mPinnedStackController.setIsMinimized(isMinimized);
484 } catch (RemoteException e) {
485 Log.e(TAG, "Could not set minimized state", e);
486 }
Winson Chungfa7053782016-11-08 15:45:10 -0800487 }
488 }
489
490 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800491 * Sets the menu visibility.
Winson Chungfa7053782016-11-08 15:45:10 -0800492 */
Winson Chung8ec0e162017-07-07 14:49:49 -0700493 private void setMenuState(int menuState, boolean resize) {
Mady Mellor637cd482017-03-21 10:39:42 -0700494 if (menuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800495 // Save the current snap fraction and if we do not drag or move the PiP, then
496 // we store back to this snap fraction. Otherwise, we'll reset the snap
497 // fraction and snap to the closest edge
498 Rect expandedBounds = new Rect(mExpandedBounds);
499 if (resize) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800500 mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
501 mMovementBounds, mExpandedMovementBounds);
Winson Chungd2d90972017-02-28 11:40:41 -0800502 }
Mady Mellor637cd482017-03-21 10:39:42 -0700503 } else if (menuState == MENU_STATE_NONE) {
Winson Chungd2d90972017-02-28 11:40:41 -0800504 // Try and restore the PiP to the closest edge, using the saved snap fraction
505 // if possible
506 if (resize) {
Winson Chungef4dc812017-04-11 13:31:44 -0700507 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
Winson Chungbb233762017-05-15 14:20:46 -0700508 // This is a very special case: when the menu is expanded and visible,
509 // navigating to another activity can trigger auto-enter PiP, and if the
510 // revealed activity has a forced rotation set, then the controller will get
511 // updated with the new rotation of the display. However, at the same time,
512 // SystemUI will try to hide the menu by creating an animation to the normal
513 // bounds which are now stale. In such a case we defer the animation to the
514 // normal bounds until after the next onMovementBoundsChanged() call to get the
515 // bounds in the new orientation
Winson Chungef4dc812017-04-11 13:31:44 -0700516 try {
517 int displayRotation = mPinnedStackController.getDisplayRotation();
518 if (mDisplayRotation != displayRotation) {
519 mDeferResizeToNormalBoundsUntilRotation = displayRotation;
520 }
521 } catch (RemoteException e) {
522 Log.e(TAG, "Could not get display rotation from controller");
523 }
524 }
525
526 if (mDeferResizeToNormalBoundsUntilRotation == -1) {
527 Rect normalBounds = new Rect(mNormalBounds);
528 mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
529 mNormalMovementBounds, mMovementBounds, mIsMinimized,
530 false /* immediate */);
531 mSavedSnapFraction = -1f;
532 }
533 } else {
Winson Chungbb233762017-05-15 14:20:46 -0700534 // If resizing is not allowed, then the PiP should be frozen until the transition
535 // ends as well
536 setTouchEnabled(false);
Winson Chungef4dc812017-04-11 13:31:44 -0700537 mSavedSnapFraction = -1f;
Winson Chunga29eb982016-12-14 12:01:27 -0800538 }
Winson Chungfa7053782016-11-08 15:45:10 -0800539 }
Mady Mellor637cd482017-03-21 10:39:42 -0700540 mMenuState = menuState;
541 updateMovementBounds(menuState);
542 if (menuState != MENU_STATE_CLOSE) {
Chenjie Yu52cacc62017-12-08 18:11:45 -0800543 MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
Mady Mellor637cd482017-03-21 10:39:42 -0700544 }
Winson Chungfa7053782016-11-08 15:45:10 -0800545 }
546
547 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800548 * @return the motion helper.
Winson Chungfa7053782016-11-08 15:45:10 -0800549 */
Winson Chung2a82fe52017-02-02 14:43:34 -0800550 public PipMotionHelper getMotionHelper() {
551 return mMotionHelper;
Winson73bc1592016-10-18 18:47:43 -0700552 }
Winson Chungfa7053782016-11-08 15:45:10 -0800553
554 /**
Winson Chungfa7053782016-11-08 15:45:10 -0800555 * Gesture controlling normal movement of the PIP.
556 */
557 private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700558 // Whether the PiP was on the left side of the screen at the start of the gesture
559 private boolean mStartedOnLeft;
Winson Chung8ec0e162017-07-07 14:49:49 -0700560 private final Point mStartPosition = new Point();
561 private final PointF mDelta = new PointF();
Mady Mellord4e40fb2017-01-26 10:43:16 -0800562
563 @Override
564 public void onDown(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800565 if (!touchState.isUserInteracting()) {
566 return;
567 }
568
Mady Mellor15b29c72017-06-07 14:53:58 -0700569 Rect bounds = mMotionHelper.getBounds();
Winson Chung8ec0e162017-07-07 14:49:49 -0700570 mDelta.set(0f, 0f);
571 mStartPosition.set(bounds.left, bounds.top);
Mady Mellor15b29c72017-06-07 14:53:58 -0700572 mStartedOnLeft = bounds.left < mMovementBounds.centerX();
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700573 mMovementWithinMinimize = true;
574 mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
575
Mady Mellora7f69742017-02-03 11:00:20 -0800576 // If the menu is still visible, and we aren't minimized, then just poke the menu
577 // so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700578 if (mMenuState != MENU_STATE_NONE && !mIsMinimized) {
Mady Mellora7f69742017-02-03 11:00:20 -0800579 mMenuController.pokeMenu();
580 }
581
Mady Mellor60421c92017-03-29 15:27:37 -0700582 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
Mady Mellord4e40fb2017-01-26 10:43:16 -0800583 mDismissViewController.createDismissTarget();
584 mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
585 }
586 }
587
Winson Chungfa7053782016-11-08 15:45:10 -0800588 @Override
589 boolean onMove(PipTouchState touchState) {
Winson Chung85d39982017-02-24 15:21:25 -0800590 if (!touchState.isUserInteracting()) {
591 return false;
592 }
593
Winson Chung2a82fe52017-02-02 14:43:34 -0800594 if (touchState.startedDragging()) {
595 mSavedSnapFraction = -1f;
Winson Chung2a82fe52017-02-02 14:43:34 -0800596
Winson Chungb54b65b2017-04-26 14:02:13 -0700597 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
598 mHandler.removeCallbacks(mShowDismissAffordance);
599 mDismissViewController.showDismissTarget();
600 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800601 }
602
Winson Chungfa7053782016-11-08 15:45:10 -0800603 if (touchState.isDragging()) {
604 // Move the pinned stack freely
Winson Chung2a82fe52017-02-02 14:43:34 -0800605 final PointF lastDelta = touchState.getLastTouchDelta();
Winson Chung8ec0e162017-07-07 14:49:49 -0700606 float lastX = mStartPosition.x + mDelta.x;
607 float lastY = mStartPosition.y + mDelta.y;
608 float left = lastX + lastDelta.x;
609 float top = lastY + lastDelta.y;
Mady Mellor8c7dc422017-05-10 12:55:06 -0700610 if (!touchState.allowDraggingOffscreen() || !ENABLE_MINIMIZE) {
Winson Chung2a82fe52017-02-02 14:43:34 -0800611 left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
Winson Chungfa7053782016-11-08 15:45:10 -0800612 }
Mady Mellor57d22552017-03-09 15:37:13 -0800613 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
614 // Allow pip to move past bottom bounds
615 top = Math.max(mMovementBounds.top, top);
616 } else {
617 top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
618 }
Winson Chung8ec0e162017-07-07 14:49:49 -0700619
620 // Add to the cumulative delta after bounding the position
621 mDelta.x += left - lastX;
622 mDelta.y += top - lastY;
623
624 mTmpBounds.set(mMotionHelper.getBounds());
Winson Chungfa7053782016-11-08 15:45:10 -0800625 mTmpBounds.offsetTo((int) left, (int) top);
Winson Chung2a82fe52017-02-02 14:43:34 -0800626 mMotionHelper.movePip(mTmpBounds);
627
Mady Mellor81d40612017-03-10 15:14:10 -0800628 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
629 updateDismissFraction();
630 }
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700631
632 final PointF curPos = touchState.getLastTouchPosition();
633 if (mMovementWithinMinimize) {
634 // Track if movement remains near starting edge to identify swipes to minimize
635 mMovementWithinMinimize = mStartedOnLeft
636 ? curPos.x <= mMovementBounds.left + mTmpBounds.width()
637 : curPos.x >= mMovementBounds.right;
638 }
639 if (mMovementWithinDismiss) {
640 // Track if movement remains near the bottom edge to identify swipe to dismiss
641 mMovementWithinDismiss = curPos.y >= mMovementBounds.bottom;
642 }
Winson Chungfa7053782016-11-08 15:45:10 -0800643 return true;
644 }
645 return false;
646 }
647
648 @Override
649 public boolean onUp(PipTouchState touchState) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700650 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
651 // Clean up the dismiss target regardless of the touch state in case the touch
652 // enabled state changes while the user is interacting
653 cleanUpDismissTarget();
654 }
655
Winson Chung85d39982017-02-24 15:21:25 -0800656 if (!touchState.isUserInteracting()) {
657 return false;
658 }
659
Mady Mellor60421c92017-03-29 15:27:37 -0700660 final PointF vel = touchState.getVelocity();
661 final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
662 final float velocity = PointF.length(vel.x, vel.y);
663 final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
Mady Mellor8c7dc422017-05-10 12:55:06 -0700664 final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS
Mady Mellor47ba1402017-04-04 17:25:43 -0700665 && touchState.getLastTouchPosition().y >= mMovementBounds.bottom
666 && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x,
667 vel.y, isFling);
668 final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal
669 && (mMovementWithinDismiss || isUpWithinDimiss);
Mady Mellor60421c92017-03-29 15:27:37 -0700670 if (ENABLE_DISMISS_DRAG_TO_EDGE) {
Winson Chungb54b65b2017-04-26 14:02:13 -0700671 // Check if the user dragged or flung the PiP offscreen to dismiss it
672 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
Chenjie Yuae9fdf042018-02-15 10:19:32 -0800673 MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
674 PipUtils.getTopPinnedActivity(mContext, mActivityManager));
Winson Chungb54b65b2017-04-26 14:02:13 -0700675 mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
676 vel.y, mUpdateScrimListener);
Winson Chungb54b65b2017-04-26 14:02:13 -0700677 return true;
Mady Mellord4e40fb2017-01-26 10:43:16 -0800678 }
Mady Mellord4e40fb2017-01-26 10:43:16 -0800679 }
Winson Chungd2d90972017-02-28 11:40:41 -0800680
Winson Chungfa7053782016-11-08 15:45:10 -0800681 if (touchState.isDragging()) {
Mady Mellor84a0f892017-03-27 14:10:46 -0700682 final boolean isFlingToEdge = isFling && isHorizontal && mMovementWithinMinimize
683 && (mStartedOnLeft ? vel.x < 0 : vel.x > 0);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700684 if (ENABLE_MINIMIZE &&
Mady Mellor2e138782017-03-27 11:09:50 -0700685 !mIsMinimized && (mMotionHelper.shouldMinimizePip() || isFlingToEdge)) {
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800686 // Pip should be minimized
Winson Chung2a82fe52017-02-02 14:43:34 -0800687 setMinimizedStateInternal(true);
Mady Mellor637cd482017-03-21 10:39:42 -0700688 if (mMenuState == MENU_STATE_FULL) {
Winson Chungd2d90972017-02-28 11:40:41 -0800689 // If the user dragged the expanded PiP to the edge, then hiding the menu
690 // will trigger the PiP to be scaled back to the normal size with the
691 // minimize offset adjusted
692 mMenuController.hideMenu();
693 } else {
Mady Mellor81d40612017-03-10 15:14:10 -0800694 mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
695 mUpdateScrimListener);
Winson Chungd2d90972017-02-28 11:40:41 -0800696 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800697 return true;
698 }
699 if (mIsMinimized) {
Winson Chungd2d90972017-02-28 11:40:41 -0800700 // If we're dragging and it wasn't a minimize gesture then we shouldn't be
701 // minimized.
Winson Chung2a82fe52017-02-02 14:43:34 -0800702 setMinimizedStateInternal(false);
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800703 }
704
Winson Chung87e5d552017-04-05 11:49:38 -0700705 AnimatorListenerAdapter postAnimationCallback = null;
Mady Mellor637cd482017-03-21 10:39:42 -0700706 if (mMenuState != MENU_STATE_NONE) {
Winson Chung87e5d552017-04-05 11:49:38 -0700707 // If the menu is still visible, and we aren't minimized, then just poke the
708 // menu so that it will timeout after the user stops touching it
Mady Mellor637cd482017-03-21 10:39:42 -0700709 mMenuController.showMenu(mMenuState, mMotionHelper.getBounds(),
Winson Chungbb787442017-09-01 11:33:47 -0700710 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
Winson Chung87e5d552017-04-05 11:49:38 -0700711 } else {
712 // If the menu is not visible, then we can still be showing the activity for the
713 // dismiss overlay, so just finish it after the animation completes
714 postAnimationCallback = new AnimatorListenerAdapter() {
715 @Override
716 public void onAnimationEnd(Animator animation) {
717 mMenuController.hideMenu();
718 }
719 };
Winson Chungd2d90972017-02-28 11:40:41 -0800720 }
721
Mady Mellor2fbdd3b2017-03-21 17:45:00 -0700722 if (isFling) {
Mady Mellor81d40612017-03-10 15:14:10 -0800723 mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
Mady Mellor15b29c72017-06-07 14:53:58 -0700724 mUpdateScrimListener, postAnimationCallback,
725 mStartPosition);
Winson Chungfa7053782016-11-08 15:45:10 -0800726 } else {
Winson Chung87e5d552017-04-05 11:49:38 -0700727 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
728 postAnimationCallback);
Winson Chungfa7053782016-11-08 15:45:10 -0800729 }
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800730 } else if (mIsMinimized) {
731 // This was a tap, so no longer minimized
Winson Chung87e5d552017-04-05 11:49:38 -0700732 mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* updateListener */,
733 null /* animatorListener */);
Winson Chung2a82fe52017-02-02 14:43:34 -0800734 setMinimizedStateInternal(false);
Mady Mellor637cd482017-03-21 10:39:42 -0700735 } else if (mMenuState != MENU_STATE_FULL) {
Winson Chungbca03112017-08-16 10:38:15 -0700736 if (mTouchState.isDoubleTap()) {
737 // Expand to fullscreen if this is a double tap
738 mMotionHelper.expandPip();
739 } else if (!mTouchState.isWaitingForDoubleTap()) {
740 // User has stalled long enough for this not to be a drag or a double tap, just
741 // expand the menu
742 mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
743 mMovementBounds, true /* allowMenuTimeout */, willResizeMenu());
744 } else {
745 // Next touch event _may_ be the second tap for the double-tap, schedule a
746 // fallback runnable to trigger the menu if no touch event occurs before the
747 // next tap
748 mTouchState.scheduleDoubleTapTimeoutCallback();
749 }
Winson Chungfa7053782016-11-08 15:45:10 -0800750 } else {
Winson Chungbe4a8082017-04-13 13:52:04 -0700751 mMenuController.hideMenu();
Winson Chung2a82fe52017-02-02 14:43:34 -0800752 mMotionHelper.expandPip();
Winson Chungfa7053782016-11-08 15:45:10 -0800753 }
754 return true;
755 }
756 };
Mady Mellor3b10dcd2017-01-23 10:08:35 -0800757
758 /**
Winson Chung2a82fe52017-02-02 14:43:34 -0800759 * Updates the current movement bounds based on whether the menu is currently visible.
760 */
Mady Mellor637cd482017-03-21 10:39:42 -0700761 private void updateMovementBounds(int menuState) {
Winson Chunga71febe2017-05-22 11:14:22 -0700762 boolean isMenuExpanded = menuState == MENU_STATE_FULL;
763 mMovementBounds = isMenuExpanded
Winson Chung2a82fe52017-02-02 14:43:34 -0800764 ? mExpandedMovementBounds
765 : mNormalMovementBounds;
Winson Chunga71febe2017-05-22 11:14:22 -0700766 try {
767 mPinnedStackController.setMinEdgeSize(isMenuExpanded ? mExpandedShortestEdgeSize : 0);
768 } catch (RemoteException e) {
769 Log.e(TAG, "Could not set minimized state", e);
770 }
Winson Chung2a82fe52017-02-02 14:43:34 -0800771 }
Winson Chung29a78652017-02-09 18:35:26 -0800772
Winson Chungb54b65b2017-04-26 14:02:13 -0700773 /**
774 * Removes the dismiss target and cancels any pending callbacks to show it.
775 */
776 private void cleanUpDismissTarget() {
777 mHandler.removeCallbacks(mShowDismissAffordance);
778 mDismissViewController.destroyDismissTarget();
779 }
780
Winson Chung9b919412017-06-19 17:01:51 -0700781 /**
782 * Resets some states related to the touch handling.
783 */
784 private void cleanUp() {
785 if (mIsMinimized) {
786 setMinimizedStateInternal(false);
787 }
788 cleanUpDismissTarget();
789 }
790
Winson Chungbb787442017-09-01 11:33:47 -0700791 /**
792 * @return whether the menu will resize as a part of showing the full menu.
793 */
794 private boolean willResizeMenu() {
795 return mExpandedBounds.width() != mNormalBounds.width() ||
796 mExpandedBounds.height() != mNormalBounds.height();
797 }
798
Winson Chung29a78652017-02-09 18:35:26 -0800799 public void dump(PrintWriter pw, String prefix) {
800 final String innerPrefix = prefix + " ";
801 pw.println(prefix + TAG);
802 pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
803 pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
804 pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
805 pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
806 pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
Mady Mellor637cd482017-03-21 10:39:42 -0700807 pw.println(innerPrefix + "mMenuState=" + mMenuState);
Winson Chungd2d90972017-02-28 11:40:41 -0800808 pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
Winson Chung29a78652017-02-09 18:35:26 -0800809 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
810 pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
Tracy Zhou43513082018-03-08 21:58:36 -0800811 pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
812 pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
Winson Chung29a78652017-02-09 18:35:26 -0800813 pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
Mady Mellor60421c92017-03-29 15:27:37 -0700814 pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + ENABLE_DISMISS_DRAG_TO_EDGE);
Mady Mellor8c7dc422017-05-10 12:55:06 -0700815 pw.println(innerPrefix + "mEnableMinimize=" + ENABLE_MINIMIZE);
Winson Chung29a78652017-02-09 18:35:26 -0800816 mSnapAlgorithm.dump(pw, innerPrefix);
817 mTouchState.dump(pw, innerPrefix);
818 mMotionHelper.dump(pw, innerPrefix);
819 }
Phil Weaverf00cd142017-03-03 13:44:00 -0800820
Winson73bc1592016-10-18 18:47:43 -0700821}