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