• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3.pageindicators;
2 
3 import android.animation.Animator;
4 import android.animation.AnimatorListenerAdapter;
5 import android.animation.ObjectAnimator;
6 import android.animation.ValueAnimator;
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.graphics.Canvas;
10 import android.graphics.Color;
11 import android.graphics.Paint;
12 import android.os.Handler;
13 import android.os.Looper;
14 import android.support.v4.graphics.ColorUtils;
15 import android.util.AttributeSet;
16 import android.util.Log;
17 import android.util.Property;
18 import android.view.ViewConfiguration;
19 import android.widget.ImageView;
20 
21 import com.android.launcher3.Launcher;
22 import com.android.launcher3.R;
23 import com.android.launcher3.Utilities;
24 import com.android.launcher3.config.FeatureFlags;
25 import com.android.launcher3.dynamicui.ExtractedColors;
26 import com.android.launcher3.dynamicui.WallpaperColorInfo;
27 
28 /**
29  * A PageIndicator that briefly shows a fraction of a line when moving between pages.
30  *
31  * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
32  */
33 public class PageIndicatorLineCaret extends PageIndicator {
34     private static final String TAG = "PageIndicatorLine";
35 
36     private static final int[] sTempCoords = new int[2];
37 
38     private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
39     private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
40     public static final int WHITE_ALPHA = (int) (0.70f * 255);
41     public static final int BLACK_ALPHA = (int) (0.65f * 255);
42 
43     private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
44     private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
45     private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
46 
47     private ValueAnimator[] mAnimators = new ValueAnimator[3];
48 
49     private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
50 
51     private boolean mShouldAutoHide = true;
52 
53     // The alpha of the line when it is showing.
54     private int mActiveAlpha = 0;
55     // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
56     private int mToAlpha;
57     // A float value representing the number of pages, to allow for an animation when it changes.
58     private float mNumPagesFloat;
59     private int mCurrentScroll;
60     private int mTotalScroll;
61     private Paint mLinePaint;
62     private Launcher mLauncher;
63     private final int mLineHeight;
64     private ImageView mAllAppsHandle;
65 
66     private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA
67             = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") {
68         @Override
69         public Integer get(PageIndicatorLineCaret obj) {
70             return obj.mLinePaint.getAlpha();
71         }
72 
73         @Override
74         public void set(PageIndicatorLineCaret obj, Integer alpha) {
75             obj.mLinePaint.setAlpha(alpha);
76             obj.invalidate();
77         }
78     };
79 
80     private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES
81             = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") {
82         @Override
83         public Float get(PageIndicatorLineCaret obj) {
84             return obj.mNumPagesFloat;
85         }
86 
87         @Override
88         public void set(PageIndicatorLineCaret obj, Float numPages) {
89             obj.mNumPagesFloat = numPages;
90             obj.invalidate();
91         }
92     };
93 
94     private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL
95             = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") {
96         @Override
97         public Integer get(PageIndicatorLineCaret obj) {
98             return obj.mTotalScroll;
99         }
100 
101         @Override
102         public void set(PageIndicatorLineCaret obj, Integer totalScroll) {
103             obj.mTotalScroll = totalScroll;
104             obj.invalidate();
105         }
106     };
107 
108     private Runnable mHideLineRunnable = new Runnable() {
109         @Override
110         public void run() {
111             animateLineToAlpha(0);
112         }
113     };
114 
PageIndicatorLineCaret(Context context)115     public PageIndicatorLineCaret(Context context) {
116         this(context, null);
117     }
118 
PageIndicatorLineCaret(Context context, AttributeSet attrs)119     public PageIndicatorLineCaret(Context context, AttributeSet attrs) {
120         this(context, attrs, 0);
121     }
122 
PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle)123     public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) {
124         super(context, attrs, defStyle);
125 
126         Resources res = context.getResources();
127         mLinePaint = new Paint();
128         mLinePaint.setAlpha(0);
129 
130         mLauncher = Launcher.getLauncher(context);
131         mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
132         setCaretDrawable(new CaretDrawable(context));
133 
134         boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
135         mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
136         mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
137     }
138 
139     @Override
onFinishInflate()140     protected void onFinishInflate() {
141         super.onFinishInflate();
142         mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
143         mAllAppsHandle.setImageDrawable(getCaretDrawable());
144         mAllAppsHandle.setOnClickListener(mLauncher);
145         mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
146         mLauncher.setAllAppsButton(mAllAppsHandle);
147     }
148 
149     @Override
setAccessibilityDelegate(AccessibilityDelegate delegate)150     public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
151         mAllAppsHandle.setAccessibilityDelegate(delegate);
152     }
153 
154     @Override
onDraw(Canvas canvas)155     protected void onDraw(Canvas canvas) {
156         if (mTotalScroll == 0 || mNumPagesFloat == 0) {
157             return;
158         }
159 
160         // Compute and draw line rect.
161         float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
162         int availableWidth = canvas.getWidth();
163         int lineWidth = (int) (availableWidth / mNumPagesFloat);
164         int lineLeft = (int) (progress * (availableWidth - lineWidth));
165         int lineRight = lineLeft + lineWidth;
166         canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
167                 mLinePaint);
168     }
169 
170     @Override
setContentDescription(CharSequence contentDescription)171     public void setContentDescription(CharSequence contentDescription) {
172         mAllAppsHandle.setContentDescription(contentDescription);
173     }
174 
175     @Override
setScroll(int currentScroll, int totalScroll)176     public void setScroll(int currentScroll, int totalScroll) {
177         if (getAlpha() == 0) {
178             return;
179         }
180         animateLineToAlpha(mActiveAlpha);
181 
182         mCurrentScroll = currentScroll;
183         if (mTotalScroll == 0) {
184             mTotalScroll = totalScroll;
185         } else if (mTotalScroll != totalScroll) {
186             animateToTotalScroll(totalScroll);
187         } else {
188             invalidate();
189         }
190 
191         if (mShouldAutoHide) {
192             hideAfterDelay();
193         }
194     }
195 
hideAfterDelay()196     private void hideAfterDelay() {
197         mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
198         mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
199     }
200 
201     @Override
setActiveMarker(int activePage)202     public void setActiveMarker(int activePage) {
203     }
204 
205     @Override
onPageCountChanged()206     protected void onPageCountChanged() {
207         if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
208             animateToNumPages(mNumPages);
209         }
210     }
211 
setShouldAutoHide(boolean shouldAutoHide)212     public void setShouldAutoHide(boolean shouldAutoHide) {
213         mShouldAutoHide = shouldAutoHide;
214         if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
215             hideAfterDelay();
216         } else if (!shouldAutoHide) {
217             mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
218         }
219     }
220 
221     /**
222      * The line's color will be:
223      * - mostly opaque white if the hotseat is white (ignoring alpha)
224      * - mostly opaque black if the hotseat is black (ignoring alpha)
225      */
updateColor(ExtractedColors extractedColors)226     public void updateColor(ExtractedColors extractedColors) {
227         if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
228             return;
229         }
230         int originalLineAlpha = mLinePaint.getAlpha();
231         int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
232         if (color != Color.TRANSPARENT) {
233             color = ColorUtils.setAlphaComponent(color, 255);
234             if (color == Color.BLACK) {
235                 mActiveAlpha = BLACK_ALPHA;
236             } else if (color == Color.WHITE) {
237                 mActiveAlpha = WHITE_ALPHA;
238             } else {
239                 Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
240                         + Integer.toHexString(color));
241             }
242             mLinePaint.setColor(color);
243             mLinePaint.setAlpha(originalLineAlpha);
244         }
245     }
246 
animateLineToAlpha(int alpha)247     private void animateLineToAlpha(int alpha) {
248         if (alpha == mToAlpha) {
249             // Ignore the new animation if it is going to the same alpha as the current animation.
250             return;
251         }
252         mToAlpha = alpha;
253         setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
254                 LINE_ALPHA_ANIMATOR_INDEX);
255     }
256 
animateToNumPages(int numPages)257     private void animateToNumPages(int numPages) {
258         setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
259                 NUM_PAGES_ANIMATOR_INDEX);
260     }
261 
animateToTotalScroll(int totalScroll)262     private void animateToTotalScroll(int totalScroll) {
263         setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
264                 TOTAL_SCROLL_ANIMATOR_INDEX);
265     }
266 
267     /**
268      * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
269      * the animation ends.
270      *
271      * If an animator is already at the index (i.e. it is already playing), it is canceled and
272      * replaced with the new animator.
273      */
setupAndRunAnimation(ValueAnimator animator, final int animatorIndex)274     private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
275         if (mAnimators[animatorIndex] != null) {
276             mAnimators[animatorIndex].cancel();
277         }
278         mAnimators[animatorIndex] = animator;
279         mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
280             @Override
281             public void onAnimationEnd(Animator animation) {
282                 mAnimators[animatorIndex] = null;
283             }
284         });
285         mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
286         mAnimators[animatorIndex].start();
287     }
288 }
289