summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Winson Chung <winsonc@google.com> 2014-06-24 12:11:49 -0700
committer Winson Chung <winsonc@google.com> 2014-06-24 12:18:34 -0700
commitcdbbb7e33033d7ae368aa5b7007ec2b20ebdaff1 (patch)
tree42f7a57be90c29f25f4dd76827c24e515d30b6e2
parentc4e976daa210c5490bd26860c983630963092fe0 (diff)
Refactoring RecentsActivity and TaskStackViewTouchHandler.
- Pulling out logic related to system bar scrims, and touch handlers to their own classes - Pulling out shadow reflection code out of RecentsActivity - Ensuring that we poke the doze trigger when scrolling
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java127
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Utilities.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java132
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java403
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java415
7 files changed, 621 insertions, 495 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
index 037af14a7c6d..f0b2cb6cb1a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
@@ -49,7 +49,7 @@ public class DozeTrigger {
/** Starts dozing. This also resets the trigger flag. */
public void startDozing() {
- poke();
+ forcePoke();
mHasTriggered = false;
}
@@ -59,8 +59,15 @@ public class DozeTrigger {
mIsDozing = false;
}
- /** Poke this dozer to wake it up for a little bit. */
+ /** Poke this dozer to wake it up for a little bit, if it is dozing. */
public void poke() {
+ if (mIsDozing) {
+ forcePoke();
+ }
+ }
+
+ /** Poke this dozer to wake it up for a little bit. */
+ void forcePoke() {
mHandler.removeCallbacks(mDozeRunnable);
mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
mIsDozing = true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 4a02bac374a0..21704a42d7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -28,7 +28,6 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Pair;
-import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -39,6 +38,7 @@ import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.FullScreenTransitionView;
import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.recents.views.ViewAnimation;
import java.lang.reflect.InvocationTargetException;
@@ -50,6 +50,26 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
FullScreenTransitionView.FullScreenTransitionViewCallbacks {
+ FrameLayout mContainerView;
+ RecentsView mRecentsView;
+ SystemBarScrimViews mScrimViews;
+ View mEmptyView;
+ FullScreenTransitionView mFullScreenshotView;
+
+ RecentsConfiguration mConfig;
+
+ RecentsAppWidgetHost mAppWidgetHost;
+ AppWidgetProviderInfo mSearchAppWidgetInfo;
+ AppWidgetHostView mSearchAppWidgetHostView;
+
+ boolean mVisible;
+ boolean mTaskLaunched;
+
+ // Runnables to finish the Recents activity
+ FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true);
+ FinishRecentsRunnable mFinishWithoutAnimationRunnable = new FinishRecentsRunnable(false);
+ FinishRecentsRunnable mFinishLaunchHomeRunnable;
+
/**
* A Runnable to finish Recents either with/without a transition, and either by calling finish()
* or just launching the specified intent.
@@ -90,40 +110,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
}
- FrameLayout mContainerView;
- RecentsView mRecentsView;
- View mEmptyView;
- View mStatusBarScrimView;
- View mNavBarScrimView;
- FullScreenTransitionView mFullScreenshotView;
-
- RecentsConfiguration mConfig;
-
- RecentsAppWidgetHost mAppWidgetHost;
- AppWidgetProviderInfo mSearchAppWidgetInfo;
- AppWidgetHostView mSearchAppWidgetHostView;
-
- boolean mVisible;
- boolean mTaskLaunched;
-
- // Runnables to finish the Recents activity
- FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true);
- FinishRecentsRunnable mFinishWithoutAnimationRunnable = new FinishRecentsRunnable(false);
- FinishRecentsRunnable mFinishLaunchHomeRunnable;
-
- private static Method sPropertyMethod;
- static {
- try {
- Class<?> c = Class.forName("android.view.GLES20Canvas");
- sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
- if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- }
- }
-
// Broadcast receiver to handle messages from our RecentsService
BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -192,10 +178,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mConfig.launchedWithNoRecentTasks = !root.hasTasks();
// Show the scrim if we animate into Recents without window transitions
- mNavBarScrimView.setVisibility(mConfig.hasNavBarScrim() &&
- !mConfig.shouldAnimateNavBarScrim() ? View.VISIBLE : View.INVISIBLE);
- mStatusBarScrimView.setVisibility(mConfig.hasStatusBarScrim() &&
- !mConfig.shouldAnimateStatusBarScrim() ? View.VISIBLE : View.INVISIBLE);
+ mScrimViews.prepareEnterRecentsAnimation();
// Add the default no-recents layout
if (mConfig.launchedWithNoRecentTasks) {
@@ -348,14 +331,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Create the empty view
LayoutInflater inflater = LayoutInflater.from(this);
mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
- mStatusBarScrimView = inflater.inflate(R.layout.recents_status_bar_scrim, mContainerView, false);
- mStatusBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
- mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
- mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+ mScrimViews = new SystemBarScrimViews(mConfig);
+ mScrimViews.inflate(inflater, mContainerView);
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
mFullScreenshotView = new FullScreenTransitionView(this, this);
mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
@@ -364,13 +341,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Add the views to the layout
mContainerView = new FrameLayout(this);
- mContainerView.addView(mStatusBarScrimView);
+ mContainerView.addView(mScrimViews.getStatusBarScrimView());
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
mContainerView.addView(mFullScreenshotView);
}
- mContainerView.addView(mNavBarScrimView);
+ mContainerView.addView(mScrimViews.getNavBarScrimView());
setContentView(mContainerView);
// Update the recent tasks
@@ -391,10 +368,10 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
onConfigurationChange();
}
- // XXX: Update the shadows
+ // Private API calls to make the shadows look better
try {
- sPropertyMethod.invoke(null, "ambientShadowStrength", String.valueOf(35f));
- sPropertyMethod.invoke(null, "ambientRatio", String.valueOf(0.5f));
+ Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f));
+ Utilities.setShadowProperty("ambientRatio", String.valueOf(0.5f));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
@@ -589,37 +566,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
public void onEnterAnimationTriggered() {
- // Fade in the scrims
- if (mConfig.hasStatusBarScrim() && mConfig.shouldAnimateStatusBarScrim()) {
- mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
- mStatusBarScrimView.animate()
- .translationY(0)
- .setStartDelay(mConfig.taskBarEnterAnimDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mStatusBarScrimView.setVisibility(View.VISIBLE);
- }
- })
- .start();
- }
- if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
- mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
- mNavBarScrimView.animate()
- .translationY(0)
- .setStartDelay(mConfig.taskBarEnterAnimDelay)
- .setDuration(mConfig.navBarScrimEnterDuration)
- .setInterpolator(mConfig.quintOutInterpolator)
- .withStartAction(new Runnable() {
- @Override
- public void run() {
- mNavBarScrimView.setVisibility(View.VISIBLE);
- }
- })
- .start();
- }
+ // Animate the scrims in
+ mScrimViews.startEnterRecentsAnimation();
}
/**** FullScreenTransitionView.FullScreenTransitionViewCallbacks Implementation ****/
@@ -642,16 +590,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
- public void onExitAnimationTriggered() {
- // Fade out the scrim
- if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
- mNavBarScrimView.animate()
- .translationY(mNavBarScrimView.getMeasuredHeight())
- .setStartDelay(0)
- .setDuration(mConfig.taskBarExitAnimDuration)
- .setInterpolator(mConfig.fastOutSlowInInterpolator)
- .start();
- }
+ public void onExitToHomeAnimationTriggered() {
+ // Animate the scrims out
+ mScrimViews.startExitRecentsAnimation();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index 46e6ee9c8ece..25875bc6fef4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -20,8 +20,26 @@ import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
/* Common code */
public class Utilities {
+
+ // Reflection methods for altering shadows
+ private static Method sPropertyMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("android.view.GLES20Canvas");
+ sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
+ if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+
/**
* Calculates a consistent animation duration (ms) for all animations depending on the movement
* of the object being animated.
@@ -66,4 +84,10 @@ public class Utilities {
int greyscale = colorToGreyscale(color);
return (greyscale < 128) ? lightRes : darkRes;
}
+
+ /** Sets some private shadow properties. */
+ public static void setShadowProperty(String property, String value)
+ throws IllegalAccessException, InvocationTargetException {
+ sPropertyMethod.invoke(null, property, value);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 724875867faf..d06c9d2e297d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -55,7 +55,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskLaunching();
- public void onExitAnimationTriggered();
+ public void onExitToHomeAnimationTriggered();
}
RecentsConfiguration mConfig;
@@ -194,7 +194,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
ctx.postAnimationTrigger.decrement();
// Notify of the exit animation
- mCb.onExitAnimationTriggered();
+ mCb.onExitToHomeAnimationTriggered();
}
/** Adds the search bar */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
new file mode 100644
index 000000000000..932c345f246a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.android.systemui.R;
+import com.android.systemui.recents.RecentsConfiguration;
+
+/** Manages the scrims for the various system bars. */
+public class SystemBarScrimViews {
+
+ RecentsConfiguration mConfig;
+
+ View mStatusBarScrimView;
+ View mNavBarScrimView;
+
+ boolean mHasNavBarScrim;
+ boolean mShouldAnimateStatusBarScrim;
+ boolean mHasStatusBarScrim;
+ boolean mShouldAnimateNavBarScrim;
+
+ public SystemBarScrimViews(RecentsConfiguration config) {
+ mConfig = config;
+ }
+
+ /** Inflates the scrim views */
+ public void inflate(LayoutInflater inflater, ViewGroup parent) {
+ mStatusBarScrimView = inflater.inflate(R.layout.recents_status_bar_scrim, parent, false);
+ mStatusBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
+ mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, parent, false);
+ mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+ }
+
+ /**
+ * Prepares the scrim views for animating when entering Recents. This will be called before
+ * the first draw.
+ */
+ public void prepareEnterRecentsAnimation() {
+ mHasNavBarScrim = mConfig.hasNavBarScrim();
+ mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim();
+ mHasStatusBarScrim = mConfig.hasStatusBarScrim();
+ mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim();
+
+ mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
+ View.VISIBLE : View.INVISIBLE);
+ mStatusBarScrimView.setVisibility(mHasStatusBarScrim && !mShouldAnimateStatusBarScrim ?
+ View.VISIBLE : View.INVISIBLE);
+ }
+
+ /**
+ * Starts animating the scrim views when entering Recents.
+ */
+ public void startEnterRecentsAnimation() {
+ if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
+ mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
+ mStatusBarScrimView.animate()
+ .translationY(0)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .withStartAction(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarScrimView.setVisibility(View.VISIBLE);
+ }
+ })
+ .start();
+ }
+ if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
+ mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+ mNavBarScrimView.animate()
+ .translationY(0)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .withStartAction(new Runnable() {
+ @Override
+ public void run() {
+ mNavBarScrimView.setVisibility(View.VISIBLE);
+ }
+ })
+ .start();
+ }
+ }
+
+ /**
+ * Starts animating the scrim views when leaving Recents (either via launching a task, or
+ * going home).
+ */
+ public void startExitRecentsAnimation() {
+ if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
+ mNavBarScrimView.animate()
+ .translationY(mNavBarScrimView.getMeasuredHeight())
+ .setStartDelay(0)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .start();
+ }
+ }
+
+ /** Returns the status bar scrim view. */
+ public View getStatusBarScrimView() {
+ return mStatusBarScrimView;
+ }
+
+ /** Returns the nav bar scrim view. */
+ public View getNavBarScrimView() {
+ return mNavBarScrimView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 5380116b702b..74bc526f5915 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -28,10 +28,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.OverScroller;
import com.android.systemui.R;
@@ -389,11 +386,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Sets the current stack scroll */
public void setStackScroll(int value) {
mStackScroll = value;
+ mUIDozeTrigger.poke();
requestSynchronizeStackViewsWithModel();
}
/** Sets the current stack scroll without synchronizing the stack view with the model */
public void setStackScrollRaw(int value) {
mStackScroll = value;
+ mUIDozeTrigger.poke();
}
/** Sets the current stack scroll to the initial state when you first enter recents */
public void setStackScrollToInitialState() {
@@ -930,11 +929,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Pokes the dozer on user interaction. */
void onUserInteraction() {
- // If the dozer is not running, then either we have not yet laid out, or it has already
- // fallen asleep, so just let it rest.
- if (mUIDozeTrigger.isDozing()) {
- mUIDozeTrigger.poke();
- }
+ // Poke the doze trigger if it is dozing
+ mUIDozeTrigger.poke();
}
/**** TaskStackCallbacks Implementation ****/
@@ -1331,393 +1327,4 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
}
-}
-
-/* Handles touch events */
-class TaskStackViewTouchHandler implements SwipeHelper.Callback {
- static int INACTIVE_POINTER_ID = -1;
-
- TaskStackView mSv;
- VelocityTracker mVelocityTracker;
-
- boolean mIsScrolling;
-
- int mInitialMotionX, mInitialMotionY;
- int mLastMotionX, mLastMotionY;
- int mActivePointerId = INACTIVE_POINTER_ID;
- TaskView mActiveTaskView = null;
-
- int mTotalScrollMotion;
- int mMinimumVelocity;
- int mMaximumVelocity;
- // The scroll touch slop is used to calculate when we start scrolling
- int mScrollTouchSlop;
- // The page touch slop is used to calculate when we start swiping
- float mPagingTouchSlop;
-
- SwipeHelper mSwipeHelper;
- boolean mInterceptedBySwipeHelper;
-
- public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
- ViewConfiguration configuration = ViewConfiguration.get(context);
- mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
- mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
- mScrollTouchSlop = configuration.getScaledTouchSlop();
- mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
- mSv = sv;
-
-
- float densityScale = context.getResources().getDisplayMetrics().density;
- mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
- mSwipeHelper.setMinAlpha(1f);
- }
-
- /** Velocity tracker helpers */
- void initOrResetVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- }
- void initVelocityTrackerIfNotExists() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- }
- void recycleVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- /** Returns the view at the specified coordinates */
- TaskView findViewAtPoint(int x, int y) {
- int childCount = mSv.getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- TaskView tv = (TaskView) mSv.getChildAt(i);
- if (tv.getVisibility() == View.VISIBLE) {
- if (mSv.isTransformedTouchPointInView(x, y, tv)) {
- return tv;
- }
- }
- }
- return null;
- }
-
- /** Touch preprocessing for handling below */
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.TouchEvents,
- "[TaskStackViewTouchHandler|interceptTouchEvent]",
- Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
- }
-
- // Return early if we have no children
- boolean hasChildren = (mSv.getChildCount() > 0);
- if (!hasChildren) {
- return false;
- }
-
- // Pass through to swipe helper if we are swiping
- mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
- if (mInterceptedBySwipeHelper) {
- return true;
- }
-
- boolean wasScrolling = !mSv.mScroller.isFinished() ||
- (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- // Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
- // Stop the current scroll if it is still flinging
- mSv.abortScroller();
- mSv.abortBoundScrollAnimation();
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
- // Check if the scroller is finished yet
- mIsScrolling = !mSv.mScroller.isFinished();
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int y = (int) ev.getY(activePointerIndex);
- int x = (int) ev.getX(activePointerIndex);
- if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
- // Save the touch move info
- mIsScrolling = true;
- // Initialize the velocity tracker if necessary
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
- }
-
- mLastMotionX = x;
- mLastMotionY = y;
- break;
- }
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP: {
- // Animate the scroll back if we've cancelled
- mSv.animateBoundScroll();
- // Disable HW layers
- if (mIsScrolling) {
- mSv.decHwLayersRefCount("stackScroll");
- }
- // Reset the drag state and the velocity tracker
- mIsScrolling = false;
- mActivePointerId = INACTIVE_POINTER_ID;
- mActiveTaskView = null;
- mTotalScrollMotion = 0;
- recycleVelocityTracker();
- break;
- }
- }
-
- return wasScrolling || mIsScrolling;
- }
-
- /** Handles touch events once we have intercepted them */
- public boolean onTouchEvent(MotionEvent ev) {
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.TouchEvents,
- "[TaskStackViewTouchHandler|touchEvent]",
- Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
- }
-
- // Short circuit if we have no children
- boolean hasChildren = (mSv.getChildCount() > 0);
- if (!hasChildren) {
- return false;
- }
-
- // Pass through to swipe helper if we are swiping
- if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
- return true;
- }
-
- // Update the velocity tracker
- initVelocityTrackerIfNotExists();
- mVelocityTracker.addMovement(ev);
-
- int action = ev.getAction();
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- // Save the touch down info
- mInitialMotionX = mLastMotionX = (int) ev.getX();
- mInitialMotionY = mLastMotionY = (int) ev.getY();
- mActivePointerId = ev.getPointerId(0);
- mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
- // Stop the current scroll if it is still flinging
- mSv.abortScroller();
- mSv.abortBoundScrollAnimation();
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- break;
- }
- case MotionEvent.ACTION_POINTER_DOWN: {
- final int index = ev.getActionIndex();
- mActivePointerId = ev.getPointerId(index);
- mLastMotionX = (int) ev.getX(index);
- mLastMotionY = (int) ev.getY(index);
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
- int activePointerIndex = ev.findPointerIndex(mActivePointerId);
- int x = (int) ev.getX(activePointerIndex);
- int y = (int) ev.getY(activePointerIndex);
- int yTotal = Math.abs(y - mInitialMotionY);
- int deltaY = mLastMotionY - y;
- if (!mIsScrolling) {
- if (yTotal > mScrollTouchSlop) {
- mIsScrolling = true;
- // Initialize the velocity tracker
- initOrResetVelocityTracker();
- mVelocityTracker.addMovement(ev);
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- // Enable HW layers
- mSv.addHwLayersRefCount("stackScroll");
- }
- }
- if (mIsScrolling) {
- int curStackScroll = mSv.getStackScroll();
- int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
- if (overScrollAmount != 0) {
- // Bound the overscroll to a fixed amount, and inversely scale the y-movement
- // relative to how close we are to the max overscroll
- float maxOverScroll = mSv.mTaskRect.height() / 3f;
- deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
- / maxOverScroll)));
- }
- mSv.setStackScroll(curStackScroll + deltaY);
- if (mSv.isScrollOutOfBounds()) {
- mVelocityTracker.clear();
- }
- }
- mLastMotionX = x;
- mLastMotionY = y;
- mTotalScrollMotion += Math.abs(deltaY);
- break;
- }
- case MotionEvent.ACTION_UP: {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
- if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
- // Enable HW layers on the stack
- mSv.addHwLayersRefCount("flingScroll");
- // XXX: Make this animation a function of the velocity AND distance
- int overscrollRange = (int) (Math.min(1f,
- Math.abs((float) velocity / mMaximumVelocity)) *
- Constants.Values.TaskStackView.TaskStackOverscrollRange);
-
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.TouchEvents,
- "[TaskStackViewTouchHandler|fling]",
- "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
- " maxVelocity: " + mMaximumVelocity +
- " overscrollRange: " + overscrollRange,
- Console.AnsiGreen);
- }
-
- // Fling scroll
- mSv.mScroller.fling(0, mSv.getStackScroll(),
- 0, -velocity,
- 0, 0,
- mSv.mMinScroll, mSv.mMaxScroll,
- 0, overscrollRange);
- // Invalidate to kick off computeScroll
- mSv.invalidate(mSv.mStackRect);
- } else if (mSv.isScrollOutOfBounds()) {
- // Animate the scroll back into bounds
- // XXX: Make this animation a function of the velocity OR distance
- mSv.animateBoundScroll();
- }
-
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- mTotalScrollMotion = 0;
- recycleVelocityTracker();
- break;
- }
- case MotionEvent.ACTION_POINTER_UP: {
- int pointerIndex = ev.getActionIndex();
- int pointerId = ev.getPointerId(pointerIndex);
- if (pointerId == mActivePointerId) {
- // Select a new active pointer id and reset the motion state
- final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
- mActivePointerId = ev.getPointerId(newPointerIndex);
- mLastMotionX = (int) ev.getX(newPointerIndex);
- mLastMotionY = (int) ev.getY(newPointerIndex);
- mVelocityTracker.clear();
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL: {
- if (mIsScrolling) {
- // Disable HW layers
- mSv.decHwLayersRefCount("stackScroll");
- }
- if (mSv.isScrollOutOfBounds()) {
- // Animate the scroll back into bounds
- // XXX: Make this animation a function of the velocity OR distance
- mSv.animateBoundScroll();
- }
- mActivePointerId = INACTIVE_POINTER_ID;
- mIsScrolling = false;
- mTotalScrollMotion = 0;
- recycleVelocityTracker();
- break;
- }
- }
- return true;
- }
-
- /**** SwipeHelper Implementation ****/
-
- @Override
- public View getChildAtPosition(MotionEvent ev) {
- return findViewAtPoint((int) ev.getX(), (int) ev.getY());
- }
-
- @Override
- public boolean canChildBeDismissed(View v) {
- return true;
- }
-
- @Override
- public void onBeginDrag(View v) {
- // Enable HW layers
- mSv.addHwLayersRefCount("swipeBegin");
- // Disable clipping with the stack while we are swiping
- TaskView tv = (TaskView) v;
- tv.setClipViewInStack(false);
- // Disallow parents from intercepting touch events
- final ViewParent parent = mSv.getParent();
- if (parent != null) {
- parent.requestDisallowInterceptTouchEvent(true);
- }
- }
-
- @Override
- public void onSwipeChanged(View v, float delta) {
- // Do nothing
- }
-
- @Override
- public void onChildDismissed(View v) {
- TaskView tv = (TaskView) v;
- mSv.onTaskDismissed(tv);
-
- // Re-enable clipping with the stack (we will reuse this view)
- tv.setClipViewInStack(true);
-
- // Disable HW layers
- mSv.decHwLayersRefCount("swipeComplete");
- }
-
- @Override
- public void onSnapBackCompleted(View v) {
- // Re-enable clipping with the stack
- TaskView tv = (TaskView) v;
- tv.setClipViewInStack(true);
- }
-
- @Override
- public void onDragCancelled(View v) {
- // Disable HW layers
- mSv.decHwLayersRefCount("swipeCancelled");
- }
-}
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
new file mode 100644
index 000000000000..d2f18aecbcae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+
+/* Handles touch events for a TaskStackView. */
+class TaskStackViewTouchHandler implements SwipeHelper.Callback {
+ static int INACTIVE_POINTER_ID = -1;
+
+ TaskStackView mSv;
+ VelocityTracker mVelocityTracker;
+
+ boolean mIsScrolling;
+
+ int mInitialMotionX, mInitialMotionY;
+ int mLastMotionX, mLastMotionY;
+ int mActivePointerId = INACTIVE_POINTER_ID;
+ TaskView mActiveTaskView = null;
+
+ int mTotalScrollMotion;
+ int mMinimumVelocity;
+ int mMaximumVelocity;
+ // The scroll touch slop is used to calculate when we start scrolling
+ int mScrollTouchSlop;
+ // The page touch slop is used to calculate when we start swiping
+ float mPagingTouchSlop;
+
+ SwipeHelper mSwipeHelper;
+ boolean mInterceptedBySwipeHelper;
+
+ public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
+ ViewConfiguration configuration = ViewConfiguration.get(context);
+ mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ mScrollTouchSlop = configuration.getScaledTouchSlop();
+ mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+ mSv = sv;
+
+
+ float densityScale = context.getResources().getDisplayMetrics().density;
+ mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+ mSwipeHelper.setMinAlpha(1f);
+ }
+
+ /** Velocity tracker helpers */
+ void initOrResetVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ } else {
+ mVelocityTracker.clear();
+ }
+ }
+ void initVelocityTrackerIfNotExists() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ }
+ void recycleVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ }
+
+ /** Returns the view at the specified coordinates */
+ TaskView findViewAtPoint(int x, int y) {
+ int childCount = mSv.getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) mSv.getChildAt(i);
+ if (tv.getVisibility() == View.VISIBLE) {
+ if (mSv.isTransformedTouchPointInView(x, y, tv)) {
+ return tv;
+ }
+ }
+ }
+ return null;
+ }
+
+ /** Touch preprocessing for handling below */
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.TouchEvents,
+ "[TaskStackViewTouchHandler|interceptTouchEvent]",
+ Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+ }
+
+ // Return early if we have no children
+ boolean hasChildren = (mSv.getChildCount() > 0);
+ if (!hasChildren) {
+ return false;
+ }
+
+ // Pass through to swipe helper if we are swiping
+ mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
+ if (mInterceptedBySwipeHelper) {
+ return true;
+ }
+
+ boolean wasScrolling = !mSv.mScroller.isFinished() ||
+ (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
+ int action = ev.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ // Save the touch down info
+ mInitialMotionX = mLastMotionX = (int) ev.getX();
+ mInitialMotionY = mLastMotionY = (int) ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+ // Stop the current scroll if it is still flinging
+ mSv.abortScroller();
+ mSv.abortBoundScrollAnimation();
+ // Initialize the velocity tracker
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
+ // Check if the scroller is finished yet
+ mIsScrolling = !mSv.mScroller.isFinished();
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ int y = (int) ev.getY(activePointerIndex);
+ int x = (int) ev.getX(activePointerIndex);
+ if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
+ // Save the touch move info
+ mIsScrolling = true;
+ // Initialize the velocity tracker if necessary
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
+ // Disallow parents from intercepting touch events
+ final ViewParent parent = mSv.getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ // Enable HW layers
+ mSv.addHwLayersRefCount("stackScroll");
+ }
+
+ mLastMotionX = x;
+ mLastMotionY = y;
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP: {
+ // Animate the scroll back if we've cancelled
+ mSv.animateBoundScroll();
+ // Disable HW layers
+ if (mIsScrolling) {
+ mSv.decHwLayersRefCount("stackScroll");
+ }
+ // Reset the drag state and the velocity tracker
+ mIsScrolling = false;
+ mActivePointerId = INACTIVE_POINTER_ID;
+ mActiveTaskView = null;
+ mTotalScrollMotion = 0;
+ recycleVelocityTracker();
+ break;
+ }
+ }
+
+ return wasScrolling || mIsScrolling;
+ }
+
+ /** Handles touch events once we have intercepted them */
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.TouchEvents,
+ "[TaskStackViewTouchHandler|touchEvent]",
+ Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+ }
+
+ // Short circuit if we have no children
+ boolean hasChildren = (mSv.getChildCount() > 0);
+ if (!hasChildren) {
+ return false;
+ }
+
+ // Pass through to swipe helper if we are swiping
+ if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
+ return true;
+ }
+
+ // Update the velocity tracker
+ initVelocityTrackerIfNotExists();
+ mVelocityTracker.addMovement(ev);
+
+ int action = ev.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ // Save the touch down info
+ mInitialMotionX = mLastMotionX = (int) ev.getX();
+ mInitialMotionY = mLastMotionY = (int) ev.getY();
+ mActivePointerId = ev.getPointerId(0);
+ mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+ // Stop the current scroll if it is still flinging
+ mSv.abortScroller();
+ mSv.abortBoundScrollAnimation();
+ // Initialize the velocity tracker
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
+ // Disallow parents from intercepting touch events
+ final ViewParent parent = mSv.getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_DOWN: {
+ final int index = ev.getActionIndex();
+ mActivePointerId = ev.getPointerId(index);
+ mLastMotionX = (int) ev.getX(index);
+ mLastMotionY = (int) ev.getY(index);
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+ int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+ int x = (int) ev.getX(activePointerIndex);
+ int y = (int) ev.getY(activePointerIndex);
+ int yTotal = Math.abs(y - mInitialMotionY);
+ int deltaY = mLastMotionY - y;
+ if (!mIsScrolling) {
+ if (yTotal > mScrollTouchSlop) {
+ mIsScrolling = true;
+ // Initialize the velocity tracker
+ initOrResetVelocityTracker();
+ mVelocityTracker.addMovement(ev);
+ // Disallow parents from intercepting touch events
+ final ViewParent parent = mSv.getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ // Enable HW layers
+ mSv.addHwLayersRefCount("stackScroll");
+ }
+ }
+ if (mIsScrolling) {
+ int curStackScroll = mSv.getStackScroll();
+ int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
+ if (overScrollAmount != 0) {
+ // Bound the overscroll to a fixed amount, and inversely scale the y-movement
+ // relative to how close we are to the max overscroll
+ float maxOverScroll = mSv.mTaskRect.height() / 3f;
+ deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
+ / maxOverScroll)));
+ }
+ mSv.setStackScroll(curStackScroll + deltaY);
+ if (mSv.isScrollOutOfBounds()) {
+ mVelocityTracker.clear();
+ }
+ }
+ mLastMotionX = x;
+ mLastMotionY = y;
+ mTotalScrollMotion += Math.abs(deltaY);
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+ if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
+ // Enable HW layers on the stack
+ mSv.addHwLayersRefCount("flingScroll");
+ // XXX: Make this animation a function of the velocity AND distance
+ int overscrollRange = (int) (Math.min(1f,
+ Math.abs((float) velocity / mMaximumVelocity)) *
+ Constants.Values.TaskStackView.TaskStackOverscrollRange);
+
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.TouchEvents,
+ "[TaskStackViewTouchHandler|fling]",
+ "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
+ " maxVelocity: " + mMaximumVelocity +
+ " overscrollRange: " + overscrollRange,
+ Console.AnsiGreen);
+ }
+
+ // Fling scroll
+ mSv.mScroller.fling(0, mSv.getStackScroll(),
+ 0, -velocity,
+ 0, 0,
+ mSv.mMinScroll, mSv.mMaxScroll,
+ 0, overscrollRange);
+ // Invalidate to kick off computeScroll
+ mSv.invalidate(mSv.mStackRect);
+ } else if (mSv.isScrollOutOfBounds()) {
+ // Animate the scroll back into bounds
+ // XXX: Make this animation a function of the velocity OR distance
+ mSv.animateBoundScroll();
+ }
+
+ if (mIsScrolling) {
+ // Disable HW layers
+ mSv.decHwLayersRefCount("stackScroll");
+ }
+ mActivePointerId = INACTIVE_POINTER_ID;
+ mIsScrolling = false;
+ mTotalScrollMotion = 0;
+ recycleVelocityTracker();
+ break;
+ }
+ case MotionEvent.ACTION_POINTER_UP: {
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ // Select a new active pointer id and reset the motion state
+ final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+ mActivePointerId = ev.getPointerId(newPointerIndex);
+ mLastMotionX = (int) ev.getX(newPointerIndex);
+ mLastMotionY = (int) ev.getY(newPointerIndex);
+ mVelocityTracker.clear();
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ if (mIsScrolling) {
+ // Disable HW layers
+ mSv.decHwLayersRefCount("stackScroll");
+ }
+ if (mSv.isScrollOutOfBounds()) {
+ // Animate the scroll back into bounds
+ // XXX: Make this animation a function of the velocity OR distance
+ mSv.animateBoundScroll();
+ }
+ mActivePointerId = INACTIVE_POINTER_ID;
+ mIsScrolling = false;
+ mTotalScrollMotion = 0;
+ recycleVelocityTracker();
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**** SwipeHelper Implementation ****/
+
+ @Override
+ public View getChildAtPosition(MotionEvent ev) {
+ return findViewAtPoint((int) ev.getX(), (int) ev.getY());
+ }
+
+ @Override
+ public boolean canChildBeDismissed(View v) {
+ return true;
+ }
+
+ @Override
+ public void onBeginDrag(View v) {
+ // Enable HW layers
+ mSv.addHwLayersRefCount("swipeBegin");
+ // Disable clipping with the stack while we are swiping
+ TaskView tv = (TaskView) v;
+ tv.setClipViewInStack(false);
+ // Disallow parents from intercepting touch events
+ final ViewParent parent = mSv.getParent();
+ if (parent != null) {
+ parent.requestDisallowInterceptTouchEvent(true);
+ }
+ }
+
+ @Override
+ public void onSwipeChanged(View v, float delta) {
+ // Do nothing
+ }
+
+ @Override
+ public void onChildDismissed(View v) {
+ TaskView tv = (TaskView) v;
+ mSv.onTaskDismissed(tv);
+
+ // Re-enable clipping with the stack (we will reuse this view)
+ tv.setClipViewInStack(true);
+
+ // Disable HW layers
+ mSv.decHwLayersRefCount("swipeComplete");
+ }
+
+ @Override
+ public void onSnapBackCompleted(View v) {
+ // Re-enable clipping with the stack
+ TaskView tv = (TaskView) v;
+ tv.setClipViewInStack(true);
+ }
+
+ @Override
+ public void onDragCancelled(View v) {
+ // Disable HW layers
+ mSv.decHwLayersRefCount("swipeCancelled");
+ }
+}