• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.view.View;
10 import android.view.ViewGroup;
11 import android.widget.ImageView;
12 
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 = .42f;
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 
PageIndicator(Context context, AttributeSet attrs)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 
setNumPages(int numPages)47     public void setNumPages(int numPages) {
48         setVisibility(numPages > 1 ? View.VISIBLE : View.GONE);
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.colorControlActivated});
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 
setLocation(float location)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 
setPosition(int position)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 
setIndex(int index)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 
animate(int from, int to)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