Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2014 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 | |
| 17 | package com.android.systemui.recents.views; |
| 18 | |
| 19 | import android.content.Context; |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 20 | import android.view.InputDevice; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 21 | import android.view.MotionEvent; |
| 22 | import android.view.VelocityTracker; |
| 23 | import android.view.View; |
| 24 | import android.view.ViewConfiguration; |
| 25 | import android.view.ViewParent; |
Winson Chung | 5c9f4b9 | 2015-06-25 16:16:46 -0700 | [diff] [blame] | 26 | import com.android.internal.logging.MetricsLogger; |
Winson | 2536c7e | 2015-10-01 15:49:31 -0700 | [diff] [blame^] | 27 | import com.android.systemui.R; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 28 | import com.android.systemui.recents.Constants; |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 29 | import com.android.systemui.recents.Recents; |
Winson | 2536c7e | 2015-10-01 15:49:31 -0700 | [diff] [blame^] | 30 | import com.android.systemui.recents.events.EventBus; |
| 31 | import com.android.systemui.recents.events.ui.DismissTaskEvent; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 32 | |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 33 | import java.util.List; |
| 34 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 35 | /* Handles touch events for a TaskStackView. */ |
| 36 | class TaskStackViewTouchHandler implements SwipeHelper.Callback { |
| 37 | static int INACTIVE_POINTER_ID = -1; |
| 38 | |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 39 | Context mContext; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 40 | TaskStackView mSv; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 41 | TaskStackViewScroller mScroller; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 42 | VelocityTracker mVelocityTracker; |
| 43 | |
| 44 | boolean mIsScrolling; |
| 45 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 46 | float mInitialP; |
| 47 | float mLastP; |
| 48 | float mTotalPMotion; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 49 | int mInitialMotionX, mInitialMotionY; |
| 50 | int mLastMotionX, mLastMotionY; |
| 51 | int mActivePointerId = INACTIVE_POINTER_ID; |
| 52 | TaskView mActiveTaskView = null; |
| 53 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 54 | int mMinimumVelocity; |
| 55 | int mMaximumVelocity; |
| 56 | // The scroll touch slop is used to calculate when we start scrolling |
| 57 | int mScrollTouchSlop; |
| 58 | // The page touch slop is used to calculate when we start swiping |
| 59 | float mPagingTouchSlop; |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 60 | // Used to calculate when a tap is outside a task view rectangle. |
| 61 | final int mWindowTouchSlop; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 62 | |
| 63 | SwipeHelper mSwipeHelper; |
| 64 | boolean mInterceptedBySwipeHelper; |
| 65 | |
Winson Chung | ebfc698 | 2014-08-26 12:25:34 -0700 | [diff] [blame] | 66 | public TaskStackViewTouchHandler(Context context, TaskStackView sv, |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 67 | TaskStackViewScroller scroller) { |
| 68 | mContext = context; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 69 | ViewConfiguration configuration = ViewConfiguration.get(context); |
| 70 | mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); |
| 71 | mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); |
| 72 | mScrollTouchSlop = configuration.getScaledTouchSlop(); |
| 73 | mPagingTouchSlop = configuration.getScaledPagingTouchSlop(); |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 74 | mWindowTouchSlop = configuration.getScaledWindowTouchSlop(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 75 | mSv = sv; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 76 | mScroller = scroller; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 77 | |
| 78 | float densityScale = context.getResources().getDisplayMetrics().density; |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 79 | mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 80 | mSwipeHelper.setMinAlpha(1f); |
| 81 | } |
| 82 | |
| 83 | /** Velocity tracker helpers */ |
| 84 | void initOrResetVelocityTracker() { |
| 85 | if (mVelocityTracker == null) { |
| 86 | mVelocityTracker = VelocityTracker.obtain(); |
| 87 | } else { |
| 88 | mVelocityTracker.clear(); |
| 89 | } |
| 90 | } |
| 91 | void initVelocityTrackerIfNotExists() { |
| 92 | if (mVelocityTracker == null) { |
| 93 | mVelocityTracker = VelocityTracker.obtain(); |
| 94 | } |
| 95 | } |
| 96 | void recycleVelocityTracker() { |
| 97 | if (mVelocityTracker != null) { |
| 98 | mVelocityTracker.recycle(); |
| 99 | mVelocityTracker = null; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /** Returns the view at the specified coordinates */ |
| 104 | TaskView findViewAtPoint(int x, int y) { |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 105 | List<TaskView> taskViews = mSv.getTaskViews(); |
| 106 | int taskViewCount = taskViews.size(); |
| 107 | for (int i = taskViewCount - 1; i >= 0; i--) { |
| 108 | TaskView tv = taskViews.get(i); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 109 | if (tv.getVisibility() == View.VISIBLE) { |
| 110 | if (mSv.isTransformedTouchPointInView(x, y, tv)) { |
| 111 | return tv; |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | return null; |
| 116 | } |
| 117 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 118 | /** Constructs a simulated motion event for the current stack scroll. */ |
| 119 | MotionEvent createMotionEventForStackScroll(MotionEvent ev) { |
| 120 | MotionEvent pev = MotionEvent.obtainNoHistory(ev); |
| 121 | pev.setLocation(0, mScroller.progressToScrollRange(mScroller.getStackScroll())); |
| 122 | return pev; |
| 123 | } |
| 124 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 125 | /** Touch preprocessing for handling below */ |
| 126 | public boolean onInterceptTouchEvent(MotionEvent ev) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 127 | // Return early if we have no children |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 128 | boolean hasTaskViews = (mSv.getTaskViews().size() > 0); |
| 129 | if (!hasTaskViews) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 130 | return false; |
| 131 | } |
| 132 | |
| 133 | // Pass through to swipe helper if we are swiping |
| 134 | mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev); |
| 135 | if (mInterceptedBySwipeHelper) { |
| 136 | return true; |
| 137 | } |
| 138 | |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 139 | boolean wasScrolling = mScroller.isScrolling() || |
| 140 | (mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning()); |
Winson | 147ecaf | 2015-09-16 16:49:55 -0700 | [diff] [blame] | 141 | int action = ev.getAction(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 142 | switch (action & MotionEvent.ACTION_MASK) { |
| 143 | case MotionEvent.ACTION_DOWN: { |
| 144 | // Save the touch down info |
| 145 | mInitialMotionX = mLastMotionX = (int) ev.getX(); |
| 146 | mInitialMotionY = mLastMotionY = (int) ev.getY(); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 147 | mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 148 | mActivePointerId = ev.getPointerId(0); |
| 149 | mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY); |
| 150 | // Stop the current scroll if it is still flinging |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 151 | mScroller.stopScroller(); |
| 152 | mScroller.stopBoundScrollAnimation(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 153 | // Initialize the velocity tracker |
| 154 | initOrResetVelocityTracker(); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 155 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev)); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 156 | break; |
| 157 | } |
| 158 | case MotionEvent.ACTION_MOVE: { |
| 159 | if (mActivePointerId == INACTIVE_POINTER_ID) break; |
| 160 | |
Justin Harrison | 31d9837 | 2014-12-04 20:05:21 +0000 | [diff] [blame] | 161 | // Initialize the velocity tracker if necessary |
| 162 | initVelocityTrackerIfNotExists(); |
| 163 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev)); |
| 164 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 165 | int activePointerIndex = ev.findPointerIndex(mActivePointerId); |
| 166 | int y = (int) ev.getY(activePointerIndex); |
| 167 | int x = (int) ev.getX(activePointerIndex); |
| 168 | if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) { |
| 169 | // Save the touch move info |
| 170 | mIsScrolling = true; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 171 | // Disallow parents from intercepting touch events |
| 172 | final ViewParent parent = mSv.getParent(); |
| 173 | if (parent != null) { |
| 174 | parent.requestDisallowInterceptTouchEvent(true); |
| 175 | } |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 176 | } |
| 177 | |
| 178 | mLastMotionX = x; |
| 179 | mLastMotionY = y; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 180 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 181 | break; |
| 182 | } |
| 183 | case MotionEvent.ACTION_CANCEL: |
| 184 | case MotionEvent.ACTION_UP: { |
| 185 | // Animate the scroll back if we've cancelled |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 186 | mScroller.animateBoundScroll(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 187 | // Reset the drag state and the velocity tracker |
| 188 | mIsScrolling = false; |
| 189 | mActivePointerId = INACTIVE_POINTER_ID; |
| 190 | mActiveTaskView = null; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 191 | mTotalPMotion = 0; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 192 | recycleVelocityTracker(); |
| 193 | break; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | return wasScrolling || mIsScrolling; |
| 198 | } |
| 199 | |
| 200 | /** Handles touch events once we have intercepted them */ |
| 201 | public boolean onTouchEvent(MotionEvent ev) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 202 | // Short circuit if we have no children |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 203 | boolean hasTaskViews = (mSv.getTaskViews().size() > 0); |
| 204 | if (!hasTaskViews) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 205 | return false; |
| 206 | } |
| 207 | |
| 208 | // Pass through to swipe helper if we are swiping |
| 209 | if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) { |
| 210 | return true; |
| 211 | } |
| 212 | |
| 213 | // Update the velocity tracker |
| 214 | initVelocityTrackerIfNotExists(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 215 | |
Winson | 147ecaf | 2015-09-16 16:49:55 -0700 | [diff] [blame] | 216 | int action = ev.getAction(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 217 | switch (action & MotionEvent.ACTION_MASK) { |
| 218 | case MotionEvent.ACTION_DOWN: { |
| 219 | // Save the touch down info |
| 220 | mInitialMotionX = mLastMotionX = (int) ev.getX(); |
| 221 | mInitialMotionY = mLastMotionY = (int) ev.getY(); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 222 | mInitialP = mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 223 | mActivePointerId = ev.getPointerId(0); |
| 224 | mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY); |
| 225 | // Stop the current scroll if it is still flinging |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 226 | mScroller.stopScroller(); |
| 227 | mScroller.stopBoundScrollAnimation(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 228 | // Initialize the velocity tracker |
| 229 | initOrResetVelocityTracker(); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 230 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev)); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 231 | // Disallow parents from intercepting touch events |
| 232 | final ViewParent parent = mSv.getParent(); |
| 233 | if (parent != null) { |
| 234 | parent.requestDisallowInterceptTouchEvent(true); |
| 235 | } |
| 236 | break; |
| 237 | } |
| 238 | case MotionEvent.ACTION_POINTER_DOWN: { |
| 239 | final int index = ev.getActionIndex(); |
| 240 | mActivePointerId = ev.getPointerId(index); |
| 241 | mLastMotionX = (int) ev.getX(index); |
| 242 | mLastMotionY = (int) ev.getY(index); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 243 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 244 | break; |
| 245 | } |
| 246 | case MotionEvent.ACTION_MOVE: { |
| 247 | if (mActivePointerId == INACTIVE_POINTER_ID) break; |
| 248 | |
Justin Harrison | 31d9837 | 2014-12-04 20:05:21 +0000 | [diff] [blame] | 249 | mVelocityTracker.addMovement(createMotionEventForStackScroll(ev)); |
| 250 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 251 | int activePointerIndex = ev.findPointerIndex(mActivePointerId); |
| 252 | int x = (int) ev.getX(activePointerIndex); |
| 253 | int y = (int) ev.getY(activePointerIndex); |
| 254 | int yTotal = Math.abs(y - mInitialMotionY); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 255 | float curP = mSv.mLayoutAlgorithm.screenYToCurveProgress(y); |
| 256 | float deltaP = mLastP - curP; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 257 | if (!mIsScrolling) { |
| 258 | if (yTotal > mScrollTouchSlop) { |
| 259 | mIsScrolling = true; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 260 | // Disallow parents from intercepting touch events |
| 261 | final ViewParent parent = mSv.getParent(); |
| 262 | if (parent != null) { |
| 263 | parent.requestDisallowInterceptTouchEvent(true); |
| 264 | } |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 265 | } |
| 266 | } |
| 267 | if (mIsScrolling) { |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 268 | float curStackScroll = mScroller.getStackScroll(); |
| 269 | float overScrollAmount = mScroller.getScrollAmountOutOfBounds(curStackScroll + deltaP); |
| 270 | if (Float.compare(overScrollAmount, 0f) != 0) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 271 | // Bound the overscroll to a fixed amount, and inversely scale the y-movement |
| 272 | // relative to how close we are to the max overscroll |
Winson | 35f3050 | 2015-09-28 11:24:36 -0700 | [diff] [blame] | 273 | float maxOverScroll = mContext.getResources().getFloat( |
| 274 | R.dimen.recents_stack_overscroll_percentage); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 275 | deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount) |
| 276 | / maxOverScroll)); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 277 | } |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 278 | mScroller.setStackScroll(curStackScroll + deltaP); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 279 | } |
| 280 | mLastMotionX = x; |
| 281 | mLastMotionY = y; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 282 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
| 283 | mTotalPMotion += Math.abs(deltaP); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 284 | break; |
| 285 | } |
| 286 | case MotionEvent.ACTION_UP: { |
Winson Chung | 2cf8b22 | 2015-01-20 11:44:05 -0800 | [diff] [blame] | 287 | mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); |
| 288 | int velocity = (int) mVelocityTracker.getYVelocity(mActivePointerId); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 289 | if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) { |
Winson Chung | 2cf8b22 | 2015-01-20 11:44:05 -0800 | [diff] [blame] | 290 | float overscrollRangePct = Math.abs((float) velocity / mMaximumVelocity); |
| 291 | int overscrollRange = (int) (Math.min(1f, overscrollRangePct) * |
| 292 | (Constants.Values.TaskStackView.TaskStackMaxOverscrollRange - |
| 293 | Constants.Values.TaskStackView.TaskStackMinOverscrollRange)); |
| 294 | mScroller.mScroller.fling(0, |
| 295 | mScroller.progressToScrollRange(mScroller.getStackScroll()), |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 296 | 0, velocity, |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 297 | 0, 0, |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 298 | mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMinScrollP), |
| 299 | mScroller.progressToScrollRange(mSv.mLayoutAlgorithm.mMaxScrollP), |
Winson Chung | 2cf8b22 | 2015-01-20 11:44:05 -0800 | [diff] [blame] | 300 | 0, Constants.Values.TaskStackView.TaskStackMinOverscrollRange + |
| 301 | overscrollRange); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 302 | // Invalidate to kick off computeScroll |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 303 | mSv.invalidate(); |
Winson Chung | fce2374 | 2015-07-30 17:33:31 -0700 | [diff] [blame] | 304 | } else if (mIsScrolling && mScroller.isScrollOutOfBounds()) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 305 | // Animate the scroll back into bounds |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 306 | mScroller.animateBoundScroll(); |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 307 | } else if (mActiveTaskView == null) { |
| 308 | // This tap didn't start on a task. |
| 309 | maybeHideRecentsFromBackgroundTap((int) ev.getX(), (int) ev.getY()); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 310 | } |
| 311 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 312 | mActivePointerId = INACTIVE_POINTER_ID; |
| 313 | mIsScrolling = false; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 314 | mTotalPMotion = 0; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 315 | recycleVelocityTracker(); |
| 316 | break; |
| 317 | } |
| 318 | case MotionEvent.ACTION_POINTER_UP: { |
| 319 | int pointerIndex = ev.getActionIndex(); |
| 320 | int pointerId = ev.getPointerId(pointerIndex); |
| 321 | if (pointerId == mActivePointerId) { |
| 322 | // Select a new active pointer id and reset the motion state |
| 323 | final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; |
| 324 | mActivePointerId = ev.getPointerId(newPointerIndex); |
| 325 | mLastMotionX = (int) ev.getX(newPointerIndex); |
| 326 | mLastMotionY = (int) ev.getY(newPointerIndex); |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 327 | mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 328 | mVelocityTracker.clear(); |
| 329 | } |
| 330 | break; |
| 331 | } |
| 332 | case MotionEvent.ACTION_CANCEL: { |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 333 | if (mScroller.isScrollOutOfBounds()) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 334 | // Animate the scroll back into bounds |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 335 | mScroller.animateBoundScroll(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 336 | } |
| 337 | mActivePointerId = INACTIVE_POINTER_ID; |
| 338 | mIsScrolling = false; |
Winson Chung | 012ef36 | 2014-07-31 18:36:25 -0700 | [diff] [blame] | 339 | mTotalPMotion = 0; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 340 | recycleVelocityTracker(); |
| 341 | break; |
| 342 | } |
| 343 | } |
| 344 | return true; |
| 345 | } |
| 346 | |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 347 | /** Hides recents if the up event at (x, y) is a tap on the background area. */ |
| 348 | void maybeHideRecentsFromBackgroundTap(int x, int y) { |
| 349 | // Ignore the up event if it's too far from its start position. The user might have been |
| 350 | // trying to scroll or swipe. |
| 351 | int dx = Math.abs(mInitialMotionX - x); |
| 352 | int dy = Math.abs(mInitialMotionY - y); |
| 353 | if (dx > mScrollTouchSlop || dy > mScrollTouchSlop) { |
| 354 | return; |
| 355 | } |
| 356 | |
| 357 | // Shift the tap position toward the center of the task stack and check to see if it would |
| 358 | // have hit a view. The user might have tried to tap on a task and missed slightly. |
| 359 | int shiftedX = x; |
Winson | 147ecaf | 2015-09-16 16:49:55 -0700 | [diff] [blame] | 360 | if (x > (mSv.getRight() - mSv.getLeft()) / 2) { |
James Cook | 4bd79b7 | 2015-03-17 07:33:28 -0700 | [diff] [blame] | 361 | shiftedX -= mWindowTouchSlop; |
| 362 | } else { |
| 363 | shiftedX += mWindowTouchSlop; |
| 364 | } |
| 365 | if (findViewAtPoint(shiftedX, y) != null) { |
| 366 | return; |
| 367 | } |
| 368 | |
| 369 | // The user intentionally tapped on the background, which is like a tap on the "desktop". |
| 370 | // Hide recents and transition to the launcher. |
| 371 | Recents recents = Recents.getInstanceAndStartIfNeeded(mSv.getContext()); |
| 372 | recents.hideRecents(false /* altTab */, true /* homeKey */); |
| 373 | } |
| 374 | |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 375 | /** Handles generic motion events */ |
| 376 | public boolean onGenericMotionEvent(MotionEvent ev) { |
| 377 | if ((ev.getSource() & InputDevice.SOURCE_CLASS_POINTER) == |
| 378 | InputDevice.SOURCE_CLASS_POINTER) { |
| 379 | int action = ev.getAction(); |
| 380 | switch (action & MotionEvent.ACTION_MASK) { |
| 381 | case MotionEvent.ACTION_SCROLL: |
| 382 | // Find the front most task and scroll the next task to the front |
| 383 | float vScroll = ev.getAxisValue(MotionEvent.AXIS_VSCROLL); |
| 384 | if (vScroll > 0) { |
Winson Chung | 83ea6f7 | 2015-06-17 13:00:23 -0700 | [diff] [blame] | 385 | if (mSv.ensureFocusedTask(true)) { |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 386 | mSv.focusNextTask(true, false); |
| 387 | } |
| 388 | } else { |
Winson Chung | 83ea6f7 | 2015-06-17 13:00:23 -0700 | [diff] [blame] | 389 | if (mSv.ensureFocusedTask(true)) { |
Winson Chung | d213a1e | 2014-10-02 11:18:30 -0700 | [diff] [blame] | 390 | mSv.focusNextTask(false, false); |
| 391 | } |
| 392 | } |
| 393 | return true; |
| 394 | } |
| 395 | } |
| 396 | return false; |
| 397 | } |
| 398 | |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 399 | /**** SwipeHelper Implementation ****/ |
| 400 | |
| 401 | @Override |
| 402 | public View getChildAtPosition(MotionEvent ev) { |
| 403 | return findViewAtPoint((int) ev.getX(), (int) ev.getY()); |
| 404 | } |
| 405 | |
| 406 | @Override |
| 407 | public boolean canChildBeDismissed(View v) { |
| 408 | return true; |
| 409 | } |
| 410 | |
| 411 | @Override |
| 412 | public void onBeginDrag(View v) { |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 413 | TaskView tv = (TaskView) v; |
Winson Chung | 8eaeb7d | 2014-06-25 15:10:59 -0700 | [diff] [blame] | 414 | // Disable clipping with the stack while we are swiping |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 415 | tv.setClipViewInStack(false); |
Winson Chung | 8eaeb7d | 2014-06-25 15:10:59 -0700 | [diff] [blame] | 416 | // Disallow touch events from this task view |
Winson Chung | 1f24c7e | 2014-07-11 17:06:48 -0700 | [diff] [blame] | 417 | tv.setTouchEnabled(false); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 418 | // Disallow parents from intercepting touch events |
| 419 | final ViewParent parent = mSv.getParent(); |
| 420 | if (parent != null) { |
| 421 | parent.requestDisallowInterceptTouchEvent(true); |
| 422 | } |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 423 | // Fade out the dismiss button |
| 424 | mSv.hideDismissAllButton(null); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | @Override |
| 428 | public void onSwipeChanged(View v, float delta) { |
| 429 | // Do nothing |
| 430 | } |
| 431 | |
| 432 | @Override |
| 433 | public void onChildDismissed(View v) { |
| 434 | TaskView tv = (TaskView) v; |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 435 | // Re-enable clipping with the stack (we will reuse this view) |
| 436 | tv.setClipViewInStack(true); |
Winson Chung | 3e3365b | 2014-07-17 18:39:02 -0700 | [diff] [blame] | 437 | // Re-enable touch events from this task view |
| 438 | tv.setTouchEnabled(true); |
Winson Chung | 8eaeb7d | 2014-06-25 15:10:59 -0700 | [diff] [blame] | 439 | // Remove the task view from the stack |
Winson | 2536c7e | 2015-10-01 15:49:31 -0700 | [diff] [blame^] | 440 | EventBus.getDefault().send(new DismissTaskEvent(tv.getTask(), tv)); |
Winson Chung | 5c9f4b9 | 2015-06-25 16:16:46 -0700 | [diff] [blame] | 441 | // Keep track of deletions by keyboard |
| 442 | MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source", |
| 443 | Constants.Metrics.DismissSourceSwipeGesture); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 444 | } |
| 445 | |
| 446 | @Override |
| 447 | public void onSnapBackCompleted(View v) { |
Winson Chung | 8eaeb7d | 2014-06-25 15:10:59 -0700 | [diff] [blame] | 448 | TaskView tv = (TaskView) v; |
Winson Chung | 8eaeb7d | 2014-06-25 15:10:59 -0700 | [diff] [blame] | 449 | // Re-enable clipping with the stack |
| 450 | tv.setClipViewInStack(true); |
| 451 | // Re-enable touch events from this task view |
Winson Chung | 1f24c7e | 2014-07-11 17:06:48 -0700 | [diff] [blame] | 452 | tv.setTouchEnabled(true); |
Winson Chung | 6ac8bd6 | 2015-01-07 16:38:35 -0800 | [diff] [blame] | 453 | // Restore the dismiss button |
| 454 | mSv.showDismissAllButton(); |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 455 | } |
Winson Chung | 7aceb9a | 2014-07-03 13:38:01 -0700 | [diff] [blame] | 456 | |
| 457 | @Override |
| 458 | public void onDragCancelled(View v) { |
| 459 | // Do nothing |
| 460 | } |
Winson Chung | cdbbb7e | 2014-06-24 12:11:49 -0700 | [diff] [blame] | 461 | } |