diff options
| author | 2014-06-24 12:11:49 -0700 | |
|---|---|---|
| committer | 2014-06-24 12:18:34 -0700 | |
| commit | cdbbb7e33033d7ae368aa5b7007ec2b20ebdaff1 (patch) | |
| tree | 42f7a57be90c29f25f4dd76827c24e515d30b6e2 | |
| parent | c4e976daa210c5490bd26860c983630963092fe0 (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
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"); + } +} |