TaskStackViewScroller.java revision 116b2c2c5430122ad7dfa14470bc640bf3c1c59f
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 17package com.android.systemui.recents.views; 18 19import android.animation.Animator; 20import android.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.Context; 23import android.util.Log; 24import android.view.animation.AnimationUtils; 25import android.view.animation.Interpolator; 26import android.widget.OverScroller; 27import com.android.systemui.R; 28import com.android.systemui.recents.misc.Utilities; 29 30/* The scrolling logic for a TaskStackView */ 31public class TaskStackViewScroller { 32 33 private static final String TAG = "TaskStackViewScroller"; 34 private static final boolean DEBUG = false; 35 36 public interface TaskStackViewScrollerCallbacks { 37 void onScrollChanged(float prevScroll, float curScroll); 38 } 39 40 Context mContext; 41 TaskStackLayoutAlgorithm mLayoutAlgorithm; 42 TaskStackViewScrollerCallbacks mCb; 43 44 float mStackScrollP; 45 float mFlingDownScrollP; 46 int mFlingDownY; 47 48 OverScroller mScroller; 49 ObjectAnimator mScrollAnimator; 50 float mFinalAnimatedScroll; 51 52 private Interpolator mLinearOutSlowInInterpolator; 53 54 public TaskStackViewScroller(Context context, TaskStackLayoutAlgorithm layoutAlgorithm) { 55 mContext = context; 56 mScroller = new OverScroller(context); 57 mLayoutAlgorithm = layoutAlgorithm; 58 mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 59 com.android.internal.R.interpolator.linear_out_slow_in); 60 setStackScroll(getStackScroll()); 61 } 62 63 /** Resets the task scroller. */ 64 void reset() { 65 mStackScrollP = 0f; 66 } 67 68 /** Sets the callbacks */ 69 void setCallbacks(TaskStackViewScrollerCallbacks cb) { 70 mCb = cb; 71 } 72 73 /** Gets the current stack scroll */ 74 public float getStackScroll() { 75 return mStackScrollP; 76 } 77 78 /** Sets the current stack scroll */ 79 public void setStackScroll(float s) { 80 float prevStackScroll = mStackScrollP; 81 mStackScrollP = s; 82 if (mCb != null) { 83 mCb.onScrollChanged(prevStackScroll, mStackScrollP); 84 } 85 } 86 87 /** 88 * Sets the current stack scroll to the initial state when you first enter recents. 89 * @return whether the stack progress changed. 90 */ 91 public boolean setStackScrollToInitialState() { 92 float prevStackScrollP = mStackScrollP; 93 setStackScroll(getBoundedStackScroll(mLayoutAlgorithm.mInitialScrollP)); 94 return Float.compare(prevStackScrollP, mStackScrollP) != 0; 95 } 96 97 /** 98 * Starts a fling that is coordinated with the {@link TaskStackViewTouchHandler}. 99 */ 100 public void fling(float downScrollP, int downY, int y, int velY, int minY, int maxY, 101 int overscroll) { 102 if (DEBUG) { 103 Log.d(TAG, "fling: " + downScrollP + ", downY: " + downY + ", y: " + y + 104 ", velY: " + velY + ", minY: " + minY + ", maxY: " + maxY); 105 } 106 mFlingDownScrollP = downScrollP; 107 mFlingDownY = downY; 108 mScroller.fling(0, y, 0, velY, 0, 0, minY, maxY, 0, overscroll); 109 } 110 111 /** Bounds the current scroll if necessary */ 112 public boolean boundScroll() { 113 float curScroll = getStackScroll(); 114 float newScroll = getBoundedStackScroll(curScroll); 115 if (Float.compare(newScroll, curScroll) != 0) { 116 setStackScroll(newScroll); 117 return true; 118 } 119 return false; 120 } 121 122 /** Returns the bounded stack scroll */ 123 float getBoundedStackScroll(float scroll) { 124 return Math.max(mLayoutAlgorithm.mMinScrollP, 125 Math.min(mLayoutAlgorithm.mMaxScrollP, scroll)); 126 } 127 128 /** Returns the amount that the absolute value of how much the scroll is out of bounds. */ 129 float getScrollAmountOutOfBounds(float scroll) { 130 if (scroll < mLayoutAlgorithm.mMinScrollP) { 131 return Math.abs(scroll - mLayoutAlgorithm.mMinScrollP); 132 } else if (scroll > mLayoutAlgorithm.mMaxScrollP) { 133 return Math.abs(scroll - mLayoutAlgorithm.mMaxScrollP); 134 } 135 return 0f; 136 } 137 138 /** Returns whether the specified scroll is out of bounds */ 139 boolean isScrollOutOfBounds() { 140 return Float.compare(getScrollAmountOutOfBounds(mStackScrollP), 0f) != 0; 141 } 142 143 /** Animates the stack scroll into bounds */ 144 ObjectAnimator animateBoundScroll() { 145 float curScroll = getStackScroll(); 146 float newScroll = getBoundedStackScroll(curScroll); 147 if (Float.compare(newScroll, curScroll) != 0) { 148 // Start a new scroll animation 149 animateScroll(curScroll, newScroll, null); 150 } 151 return mScrollAnimator; 152 } 153 154 /** Animates the stack scroll */ 155 void animateScroll(float curScroll, float newScroll, final Runnable postRunnable) { 156 // Finish any current scrolling animations 157 if (mScrollAnimator != null && mScrollAnimator.isRunning()) { 158 setStackScroll(mFinalAnimatedScroll); 159 mScroller.startScroll(0, progressToScrollRange(mFinalAnimatedScroll), 0, 0, 0); 160 } 161 stopScroller(); 162 stopBoundScrollAnimation(); 163 164 mFinalAnimatedScroll = newScroll; 165 mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll); 166 mScrollAnimator.setDuration(mContext.getResources().getInteger( 167 R.integer.recents_animate_task_stack_scroll_duration)); 168 mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator); 169 mScrollAnimator.addListener(new AnimatorListenerAdapter() { 170 @Override 171 public void onAnimationEnd(Animator animation) { 172 if (postRunnable != null) { 173 postRunnable.run(); 174 } 175 mScrollAnimator.removeAllListeners(); 176 } 177 }); 178 mScrollAnimator.start(); 179 } 180 181 /** Aborts any current stack scrolls */ 182 void stopBoundScrollAnimation() { 183 Utilities.cancelAnimationWithoutCallbacks(mScrollAnimator); 184 } 185 186 /**** OverScroller ****/ 187 188 // TODO: Remove 189 @Deprecated 190 int progressToScrollRange(float p) { 191 return (int) (p * mLayoutAlgorithm.mStackRect.height()); 192 } 193 194 /** Called from the view draw, computes the next scroll. */ 195 boolean computeScroll() { 196 if (mScroller.computeScrollOffset()) { 197 float deltaP = mLayoutAlgorithm.getDeltaPForY(mFlingDownY, mScroller.getCurrY()); 198 float scroll = mFlingDownScrollP + deltaP; 199 setStackScroll(scroll); 200 if (DEBUG) { 201 Log.d(TAG, "computeScroll: " + scroll); 202 } 203 return true; 204 } 205 return false; 206 } 207 208 /** Returns whether the overscroller is scrolling. */ 209 boolean isScrolling() { 210 return !mScroller.isFinished(); 211 } 212 213 /** Stops the scroller and any current fling. */ 214 void stopScroller() { 215 if (!mScroller.isFinished()) { 216 mScroller.abortAnimation(); 217 } 218 } 219}