blob: 08e5f721a141c25c45f9dc574f8a8386f8eb80ac [file] [log] [blame]
Winson Chungd2be3812013-07-16 11:11:32 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher3;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.LayoutTransition;
Winson Chung5bc865e2013-07-18 15:18:25 -070022import android.animation.TimeInterpolator;
Winson Chungd2be3812013-07-16 11:11:32 -070023import android.content.ComponentName;
24import android.content.Context;
25import android.content.res.Configuration;
26import android.content.res.Resources;
27import android.content.res.TypedArray;
28import android.util.AttributeSet;
29import android.util.Log;
30import android.view.LayoutInflater;
31import android.view.View;
32import android.view.ViewGroup;
33import android.widget.LinearLayout;
34import com.android.launcher3.R;
35
36import java.util.ArrayList;
37
38public class PageIndicator extends LinearLayout {
39 @SuppressWarnings("unused")
40 private static final String TAG = "PageIndicator";
Winson Chung5bc865e2013-07-18 15:18:25 -070041 // Want this to look good? Keep it odd
42 private static final boolean MODULATE_ALPHA_ENABLED = false;
Winson Chungd2be3812013-07-16 11:11:32 -070043
44 private LayoutInflater mLayoutInflater;
Winson Chung5bc865e2013-07-18 15:18:25 -070045 private int[] mWindowRange = new int[2];
46 private int mMaxWindowSize;
47
48 private ArrayList<PageIndicatorMarker> mMarkers =
49 new ArrayList<PageIndicatorMarker>();
50 private int mActiveMarkerIndex;
51
Winson Chung7819a562013-09-19 15:55:45 -070052 public static class PageMarkerResources {
53 int activeId;
54 int inactiveId;
55
56 public PageMarkerResources() {
57 activeId = R.drawable.ic_pageindicator_current;
58 inactiveId = R.drawable.ic_pageindicator_default;
59 }
60 public PageMarkerResources(int aId, int iaId) {
61 activeId = aId;
62 inactiveId = iaId;
63 }
64 }
65
Winson Chungd2be3812013-07-16 11:11:32 -070066 public PageIndicator(Context context) {
67 this(context, null);
68 }
69
70 public PageIndicator(Context context, AttributeSet attrs) {
71 this(context, attrs, 0);
72 }
73
74 public PageIndicator(Context context, AttributeSet attrs, int defStyle) {
75 super(context, attrs, defStyle);
Winson Chung5bc865e2013-07-18 15:18:25 -070076 TypedArray a = context.obtainStyledAttributes(attrs,
77 R.styleable.PageIndicator, defStyle, 0);
78 mMaxWindowSize = a.getInteger(R.styleable.PageIndicator_windowSize, 15);
79 mWindowRange[0] = 0;
80 mWindowRange[1] = 0;
Winson Chungd2be3812013-07-16 11:11:32 -070081 mLayoutInflater = LayoutInflater.from(context);
Winson Chung5bc865e2013-07-18 15:18:25 -070082 a.recycle();
Winson Chungd2be3812013-07-16 11:11:32 -070083
Winson Chung5bc865e2013-07-18 15:18:25 -070084 // Set the layout transition properties
Winson Chungd2be3812013-07-16 11:11:32 -070085 LayoutTransition transition = getLayoutTransition();
Winson Chung5bc865e2013-07-18 15:18:25 -070086 transition.setDuration(175);
87 }
88
89 private void enableLayoutTransitions() {
90 LayoutTransition transition = getLayoutTransition();
91 transition.enableTransitionType(LayoutTransition.APPEARING);
92 transition.enableTransitionType(LayoutTransition.DISAPPEARING);
93 transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
94 transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
95 }
96
97 private void disableLayoutTransitions() {
98 LayoutTransition transition = getLayoutTransition();
99 transition.disableTransitionType(LayoutTransition.APPEARING);
100 transition.disableTransitionType(LayoutTransition.DISAPPEARING);
101 transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
102 transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
103 }
104
105 void offsetWindowCenterTo(int activeIndex, boolean allowAnimations) {
106 if (activeIndex < 0) {
107 new Throwable().printStackTrace();
108 }
109 int windowSize = Math.min(mMarkers.size(), mMaxWindowSize);
110 int hWindowSize = (int) windowSize / 2;
111 float hfWindowSize = windowSize / 2f;
112 int windowStart = Math.max(0, activeIndex - hWindowSize);
113 int windowEnd = Math.min(mMarkers.size(), windowStart + mMaxWindowSize);
114 windowStart = windowEnd - Math.min(mMarkers.size(), windowSize);
115 int windowMid = windowStart + (windowEnd - windowStart) / 2;
116 boolean windowAtStart = (windowStart == 0);
117 boolean windowAtEnd = (windowEnd == mMarkers.size());
118 boolean windowMoved = (mWindowRange[0] != windowStart) ||
119 (mWindowRange[1] != windowEnd);
120
121 if (!allowAnimations) {
122 disableLayoutTransitions();
123 }
124
125 // Remove all the previous children that are no longer in the window
126 for (int i = getChildCount() - 1; i >= 0; --i) {
127 PageIndicatorMarker marker = (PageIndicatorMarker) getChildAt(i);
128 int markerIndex = mMarkers.indexOf(marker);
129 if (markerIndex < windowStart || markerIndex >= windowEnd) {
130 removeView(marker);
131 }
132 }
133
134 // Add all the new children that belong in the window
135 for (int i = 0; i < mMarkers.size(); ++i) {
136 PageIndicatorMarker marker = (PageIndicatorMarker) mMarkers.get(i);
137 if (windowStart <= i && i < windowEnd) {
138 if (indexOfChild(marker) < 0) {
139 addView(marker, i - windowStart);
140 }
141 if (i == activeIndex) {
142 marker.activate(windowMoved);
143 } else {
144 marker.inactivate(windowMoved);
145 }
146 } else {
147 marker.inactivate(true);
148 }
149
150 if (MODULATE_ALPHA_ENABLED) {
151 // Update the marker's alpha
152 float alpha = 1f;
153 if (mMarkers.size() > windowSize) {
154 if ((windowAtStart && i > hWindowSize) ||
155 (windowAtEnd && i < (mMarkers.size() - hWindowSize)) ||
156 (!windowAtStart && !windowAtEnd)) {
157 alpha = 1f - Math.abs((i - windowMid) / hfWindowSize);
158 }
159 }
160 marker.animate().alpha(alpha).setDuration(500).start();
161 }
162 }
163
164 if (!allowAnimations) {
165 enableLayoutTransitions();
166 }
167
168 mWindowRange[0] = windowStart;
169 mWindowRange[1] = windowEnd;
Winson Chungd2be3812013-07-16 11:11:32 -0700170 }
171
Winson Chung7819a562013-09-19 15:55:45 -0700172 void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700173 index = Math.max(0, Math.min(index, mMarkers.size()));
174
Winson Chung7819a562013-09-19 15:55:45 -0700175 PageIndicatorMarker m =
176 (PageIndicatorMarker) mLayoutInflater.inflate(R.layout.page_indicator_marker,
177 this, false);
178 m.setMarkerDrawables(marker.activeId, marker.inactiveId);
179
180 mMarkers.add(index, m);
Winson Chungc58497e2013-09-03 17:48:37 -0700181 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700182 }
Winson Chung7819a562013-09-19 15:55:45 -0700183 void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations) {
184 for (int i = 0; i < markers.size(); ++i) {
185 addMarker(Integer.MAX_VALUE, markers.get(i), allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700186 }
187 }
188
Winson Chung89f97052013-09-20 11:32:26 -0700189 void updateMarker(int index, PageMarkerResources marker) {
190 PageIndicatorMarker m = mMarkers.get(index);
191 m.setMarkerDrawables(marker.activeId, marker.inactiveId);
192 }
193
Winson Chungc58497e2013-09-03 17:48:37 -0700194 void removeMarker(int index, boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700195 if (mMarkers.size() > 0) {
196 index = Math.max(0, Math.min(mMarkers.size() - 1, index));
197 mMarkers.remove(index);
Winson Chungc58497e2013-09-03 17:48:37 -0700198 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700199 }
200 }
Winson Chungc58497e2013-09-03 17:48:37 -0700201 void removeAllMarkers(boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700202 while (mMarkers.size() > 0) {
Winson Chungc58497e2013-09-03 17:48:37 -0700203 removeMarker(Integer.MAX_VALUE, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700204 }
205 }
206
207 void setActiveMarker(int index) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700208 // Center the active marker
209 mActiveMarkerIndex = index;
210 offsetWindowCenterTo(index, false);
211 }
212
213 void dumpState(String txt) {
214 System.out.println(txt);
215 System.out.println("\tmMarkers: " + mMarkers.size());
216 for (int i = 0; i < mMarkers.size(); ++i) {
217 PageIndicatorMarker m = mMarkers.get(i);
218 System.out.println("\t\t(" + i + ") " + m);
Winson Chungd2be3812013-07-16 11:11:32 -0700219 }
Winson Chung5bc865e2013-07-18 15:18:25 -0700220 System.out.println("\twindow: [" + mWindowRange[0] + ", " + mWindowRange[1] + "]");
221 System.out.println("\tchildren: " + getChildCount());
222 for (int i = 0; i < getChildCount(); ++i) {
223 PageIndicatorMarker m = (PageIndicatorMarker) getChildAt(i);
224 System.out.println("\t\t(" + i + ") " + m);
225 }
226 System.out.println("\tactive: " + mActiveMarkerIndex);
Winson Chungd2be3812013-07-16 11:11:32 -0700227 }
228}