Home | History | Annotate | Download | only in qs
      1 package com.android.systemui.qs;
      2 
      3 import android.content.Context;
      4 import android.content.res.ColorStateList;
      5 import android.content.res.TypedArray;
      6 import android.graphics.drawable.AnimatedVectorDrawable;
      7 import android.util.AttributeSet;
      8 import android.util.Log;
      9 import android.util.TypedValue;
     10 import android.view.View;
     11 import android.view.ViewGroup;
     12 import android.widget.ImageView;
     13 import com.android.systemui.R;
     14 
     15 import java.util.ArrayList;
     16 
     17 public class PageIndicator extends ViewGroup {
     18 
     19     private static final String TAG = "PageIndicator";
     20     private static final boolean DEBUG = false;
     21 
     22     private static final long ANIMATION_DURATION = 250;
     23 
     24     // The size of a single dot in relation to the whole animation.
     25     private static final float SINGLE_SCALE = .4f;
     26 
     27     private static final float MINOR_ALPHA = .3f;
     28 
     29     private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
     30 
     31     private final int mPageIndicatorWidth;
     32     private final int mPageIndicatorHeight;
     33     private final int mPageDotWidth;
     34 
     35     private int mPosition = -1;
     36     private boolean mAnimating;
     37 
     38     public PageIndicator(Context context, AttributeSet attrs) {
     39         super(context, attrs);
     40         mPageIndicatorWidth =
     41                 (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
     42         mPageIndicatorHeight =
     43                 (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
     44         mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
     45     }
     46 
     47     public void setNumPages(int numPages) {
     48         setVisibility(numPages > 1 ? View.VISIBLE : View.INVISIBLE);
     49         if (mAnimating) {
     50             Log.w(TAG, "setNumPages during animation");
     51         }
     52         while (numPages < getChildCount()) {
     53             removeViewAt(getChildCount() - 1);
     54         }
     55         TypedArray array = getContext().obtainStyledAttributes(
     56                 new int[]{android.R.attr.colorForeground});
     57         int color = array.getColor(0, 0);
     58         array.recycle();
     59         while (numPages > getChildCount()) {
     60             ImageView v = new ImageView(mContext);
     61             v.setImageResource(R.drawable.minor_a_b);
     62             v.setImageTintList(ColorStateList.valueOf(color));
     63             addView(v, new LayoutParams(mPageIndicatorWidth, mPageIndicatorHeight));
     64         }
     65         // Refresh state.
     66         setIndex(mPosition >> 1);
     67     }
     68 
     69     public void setLocation(float location) {
     70         int index = (int) location;
     71         setContentDescription(getContext().getString(R.string.accessibility_quick_settings_page,
     72                 (index + 1), getChildCount()));
     73         int position = index << 1 | ((location != index) ? 1 : 0);
     74         if (DEBUG) Log.d(TAG, "setLocation " + location + " " + index + " " + position);
     75 
     76         int lastPosition = mPosition;
     77         if (mQueuedPositions.size() != 0) {
     78             lastPosition = mQueuedPositions.get(mQueuedPositions.size() - 1);
     79         }
     80         if (position == lastPosition) return;
     81         if (mAnimating) {
     82             if (DEBUG) Log.d(TAG, "Queueing transition to " + Integer.toHexString(position));
     83             mQueuedPositions.add(position);
     84             return;
     85         }
     86 
     87         setPosition(position);
     88     }
     89 
     90     private void setPosition(int position) {
     91         if (isVisibleToUser() && Math.abs(mPosition - position) == 1) {
     92             animate(mPosition, position);
     93         } else {
     94             if (DEBUG) Log.d(TAG, "Skipping animation " + isVisibleToUser() + " " + mPosition
     95                     + " " + position);
     96             setIndex(position >> 1);
     97         }
     98         mPosition = position;
     99     }
    100 
    101     private void setIndex(int index) {
    102         final int N = getChildCount();
    103         for (int i = 0; i < N; i++) {
    104             ImageView v = (ImageView) getChildAt(i);
    105             // Clear out any animation positioning.
    106             v.setTranslationX(0);
    107             v.setImageResource(R.drawable.major_a_b);
    108             v.setAlpha(getAlpha(i == index));
    109         }
    110     }
    111 
    112     private void animate(int from, int to) {
    113         if (DEBUG) Log.d(TAG, "Animating from " + Integer.toHexString(from) + " to "
    114                 + Integer.toHexString(to));
    115         int fromIndex = from >> 1;
    116         int toIndex = to >> 1;
    117 
    118         // Set the position of everything, then we will manually control the two views involved
    119         // in the animation.
    120         setIndex(fromIndex);
    121 
    122         boolean fromTransition = (from & 1) != 0;
    123         boolean isAState = fromTransition ? from > to : from < to;
    124         int firstIndex = Math.min(fromIndex, toIndex);
    125         int secondIndex = Math.max(fromIndex, toIndex);
    126         if (secondIndex == firstIndex) {
    127             secondIndex++;
    128         }
    129         ImageView first = (ImageView) getChildAt(firstIndex);
    130         ImageView second = (ImageView) getChildAt(secondIndex);
    131         if (first == null || second == null) {
    132             // may happen during reInflation or other weird cases
    133             return;
    134         }
    135         // Lay the two views on top of each other.
    136         second.setTranslationX(first.getX() - second.getX());
    137 
    138         playAnimation(first, getTransition(fromTransition, isAState, false));
    139         first.setAlpha(getAlpha(false));
    140 
    141         playAnimation(second, getTransition(fromTransition, isAState, true));
    142         second.setAlpha(getAlpha(true));
    143 
    144         mAnimating = true;
    145     }
    146 
    147     private float getAlpha(boolean isMajor) {
    148         return isMajor ? 1 : MINOR_ALPHA;
    149     }
    150 
    151     private void playAnimation(ImageView imageView, int res) {
    152         final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getContext().getDrawable(res);
    153         imageView.setImageDrawable(avd);
    154         avd.forceAnimationOnUI();
    155         avd.start();
    156         // TODO: Figure out how to user an AVD animation callback instead, which doesn't
    157         // seem to be working right now...
    158         postDelayed(mAnimationDone, ANIMATION_DURATION);
    159     }
    160 
    161     private int getTransition(boolean fromB, boolean isMajorAState, boolean isMajor) {
    162         if (isMajor) {
    163             if (fromB) {
    164                 if (isMajorAState) {
    165                     return R.drawable.major_b_a_animation;
    166                 } else {
    167                     return R.drawable.major_b_c_animation;
    168                 }
    169             } else {
    170                 if (isMajorAState) {
    171                     return R.drawable.major_a_b_animation;
    172                 } else {
    173                     return R.drawable.major_c_b_animation;
    174                 }
    175             }
    176         } else {
    177             if (fromB) {
    178                 if (isMajorAState) {
    179                     return R.drawable.minor_b_c_animation;
    180                 } else {
    181                     return R.drawable.minor_b_a_animation;
    182                 }
    183             } else {
    184                 if (isMajorAState) {
    185                     return R.drawable.minor_c_b_animation;
    186                 } else {
    187                     return R.drawable.minor_a_b_animation;
    188                 }
    189             }
    190         }
    191     }
    192 
    193     @Override
    194     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    195         final int N = getChildCount();
    196         if (N == 0) {
    197             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    198             return;
    199         }
    200         final int widthChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorWidth,
    201                 MeasureSpec.EXACTLY);
    202         final int heightChildSpec = MeasureSpec.makeMeasureSpec(mPageIndicatorHeight,
    203                 MeasureSpec.EXACTLY);
    204         for (int i = 0; i < N; i++) {
    205             getChildAt(i).measure(widthChildSpec, heightChildSpec);
    206         }
    207         int width = (mPageIndicatorWidth - mPageDotWidth) * (N - 1) + mPageDotWidth;
    208         setMeasuredDimension(width, mPageIndicatorHeight);
    209     }
    210 
    211     @Override
    212     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    213         final int N = getChildCount();
    214         if (N == 0) {
    215             return;
    216         }
    217         for (int i = 0; i < N; i++) {
    218             int left = (mPageIndicatorWidth - mPageDotWidth) * i;
    219             getChildAt(i).layout(left, 0, mPageIndicatorWidth + left, mPageIndicatorHeight);
    220         }
    221     }
    222 
    223     private final Runnable mAnimationDone = new Runnable() {
    224         @Override
    225         public void run() {
    226             if (DEBUG) Log.d(TAG, "onAnimationEnd - queued: " + mQueuedPositions.size());
    227             mAnimating = false;
    228             if (mQueuedPositions.size() != 0) {
    229                 setPosition(mQueuedPositions.remove(0));
    230             }
    231         }
    232     };
    233 }
    234