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