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