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