blob: 3b026bdd7f539e43521ae636d45d602d9e6bd15a [file] [log] [blame]
Adam Cohen3db40672010-07-19 22:41:57 -07001/*
2 * Copyright (C) 2010 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 android.widget;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.res.TypedArray;
24import android.os.Handler;
25import android.os.Message;
26import android.util.AttributeSet;
27import android.util.Log;
Adam Cohen2dd21972010-08-15 18:20:04 -070028import android.view.RemotableViewMethod;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080029import android.view.accessibility.AccessibilityEvent;
30import android.view.accessibility.AccessibilityNodeInfo;
Adam Cohen3db40672010-07-19 22:41:57 -070031import android.widget.RemoteViews.RemoteView;
32
33/**
34 * Simple {@link ViewAnimator} that will animate between two or more views
35 * that have been added to it. Only one child is shown at a time. If
36 * requested, can automatically flip between each child at a regular interval.
37 *
38 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
39 * @attr ref android.R.styleable#AdapterViewFlipper_autoStart
40 */
41@RemoteView
42public class AdapterViewFlipper extends AdapterViewAnimator {
43 private static final String TAG = "ViewFlipper";
44 private static final boolean LOGD = false;
45
46 private static final int DEFAULT_INTERVAL = 10000;
Adam Cohen3db40672010-07-19 22:41:57 -070047
48 private int mFlipInterval = DEFAULT_INTERVAL;
Adam Cohen3db40672010-07-19 22:41:57 -070049 private boolean mAutoStart = false;
50
51 private boolean mRunning = false;
52 private boolean mStarted = false;
53 private boolean mVisible = false;
54 private boolean mUserPresent = true;
Adam Cohena02fdf12010-11-03 13:27:40 -070055 private boolean mAdvancedByHost = false;
Adam Cohen3db40672010-07-19 22:41:57 -070056
57 public AdapterViewFlipper(Context context) {
58 super(context);
Adam Cohen3db40672010-07-19 22:41:57 -070059 }
60
61 public AdapterViewFlipper(Context context, AttributeSet attrs) {
Alan Viverette617feb92013-09-09 18:09:13 -070062 this(context, attrs, 0);
63 }
Adam Cohen3db40672010-07-19 22:41:57 -070064
Alan Viverette617feb92013-09-09 18:09:13 -070065 public AdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) {
66 this(context, attrs, defStyleAttr, 0);
67 }
68
69 public AdapterViewFlipper(
70 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
71 super(context, attrs, defStyleAttr, defStyleRes);
72
73 final TypedArray a = context.obtainStyledAttributes(attrs,
74 com.android.internal.R.styleable.AdapterViewFlipper, defStyleAttr, defStyleRes);
Adam Cohen3db40672010-07-19 22:41:57 -070075 mFlipInterval = a.getInt(
Adam Cohen472566022011-07-22 16:28:21 -070076 com.android.internal.R.styleable.AdapterViewFlipper_flipInterval, DEFAULT_INTERVAL);
Adam Cohen3db40672010-07-19 22:41:57 -070077 mAutoStart = a.getBoolean(
Adam Cohen472566022011-07-22 16:28:21 -070078 com.android.internal.R.styleable.AdapterViewFlipper_autoStart, false);
Adam Cohen1b065cd2010-09-28 14:53:47 -070079
Adam Cohen472566022011-07-22 16:28:21 -070080 // A view flipper should cycle through the views
81 mLoopViews = true;
Adam Cohen1b065cd2010-09-28 14:53:47 -070082
Adam Cohen3db40672010-07-19 22:41:57 -070083 a.recycle();
Adam Cohen3db40672010-07-19 22:41:57 -070084 }
85
86 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
87 @Override
88 public void onReceive(Context context, Intent intent) {
89 final String action = intent.getAction();
90 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
91 mUserPresent = false;
92 updateRunning();
93 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
94 mUserPresent = true;
95 updateRunning(false);
96 }
97 }
98 };
99
100 @Override
101 protected void onAttachedToWindow() {
102 super.onAttachedToWindow();
103
104 // Listen for broadcasts related to user-presence
105 final IntentFilter filter = new IntentFilter();
106 filter.addAction(Intent.ACTION_SCREEN_OFF);
107 filter.addAction(Intent.ACTION_USER_PRESENT);
108 getContext().registerReceiver(mReceiver, filter);
109
110 if (mAutoStart) {
111 // Automatically start when requested
112 startFlipping();
113 }
114 }
115
116 @Override
117 protected void onDetachedFromWindow() {
118 super.onDetachedFromWindow();
119 mVisible = false;
120
121 getContext().unregisterReceiver(mReceiver);
122 updateRunning();
123 }
124
125 @Override
126 protected void onWindowVisibilityChanged(int visibility) {
127 super.onWindowVisibilityChanged(visibility);
128 mVisible = (visibility == VISIBLE);
129 updateRunning(false);
130 }
131
132 @Override
133 public void setAdapter(Adapter adapter) {
134 super.setAdapter(adapter);
135 updateRunning();
136 }
137
138 /**
Philip Milneaac722a2012-03-26 13:30:26 -0700139 * Returns the flip interval, in milliseconds.
Adam Cohen3db40672010-07-19 22:41:57 -0700140 *
Philip Milneaac722a2012-03-26 13:30:26 -0700141 * @return the flip interval in milliseconds
142 *
143 * @see #setFlipInterval(int)
144 *
145 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
Adam Cohen3db40672010-07-19 22:41:57 -0700146 */
Philip Milneaac722a2012-03-26 13:30:26 -0700147 public int getFlipInterval() {
148 return mFlipInterval;
149 }
150
151 /**
152 * How long to wait before flipping to the next view.
153 *
154 * @param flipInterval flip interval in milliseconds
155 *
156 * @see #getFlipInterval()
157 *
158 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
159 */
160 public void setFlipInterval(int flipInterval) {
161 mFlipInterval = flipInterval;
Adam Cohen3db40672010-07-19 22:41:57 -0700162 }
163
164 /**
165 * Start a timer to cycle through child views
166 */
167 public void startFlipping() {
168 mStarted = true;
169 updateRunning();
170 }
171
172 /**
173 * No more flips
174 */
175 public void stopFlipping() {
176 mStarted = false;
177 updateRunning();
178 }
179
180 /**
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700181 * {@inheritDoc}
182 */
183 @Override
184 @RemotableViewMethod
185 public void showNext() {
186 // if the flipper is currently flipping automatically, and showNext() is called
187 // we should we should make sure to reset the timer
188 if (mRunning) {
189 mHandler.removeMessages(FLIP_MSG);
190 Message msg = mHandler.obtainMessage(FLIP_MSG);
191 mHandler.sendMessageDelayed(msg, mFlipInterval);
192 }
193 super.showNext();
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 @Override
200 @RemotableViewMethod
201 public void showPrevious() {
202 // if the flipper is currently flipping automatically, and showPrevious() is called
203 // we should we should make sure to reset the timer
204 if (mRunning) {
205 mHandler.removeMessages(FLIP_MSG);
206 Message msg = mHandler.obtainMessage(FLIP_MSG);
207 mHandler.sendMessageDelayed(msg, mFlipInterval);
208 }
209 super.showPrevious();
210 }
211
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700212 /**
Adam Cohen3db40672010-07-19 22:41:57 -0700213 * Internal method to start or stop dispatching flip {@link Message} based
214 * on {@link #mRunning} and {@link #mVisible} state.
215 */
216 private void updateRunning() {
217 // by default when we update running, we want the
218 // current view to animate in
219 updateRunning(true);
220 }
221
222 /**
223 * Internal method to start or stop dispatching flip {@link Message} based
224 * on {@link #mRunning} and {@link #mVisible} state.
225 *
226 * @param flipNow Determines whether or not to execute the animation now, in
227 * addition to queuing future flips. If omitted, defaults to
228 * true.
229 */
230 private void updateRunning(boolean flipNow) {
Adam Cohena02fdf12010-11-03 13:27:40 -0700231 boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
232 && mAdapter != null;
Adam Cohen3db40672010-07-19 22:41:57 -0700233 if (running != mRunning) {
234 if (running) {
235 showOnly(mWhichChild, flipNow);
236 Message msg = mHandler.obtainMessage(FLIP_MSG);
237 mHandler.sendMessageDelayed(msg, mFlipInterval);
238 } else {
239 mHandler.removeMessages(FLIP_MSG);
240 }
241 mRunning = running;
242 }
243 if (LOGD) {
244 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
245 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
246 }
247 }
248
249 /**
250 * Returns true if the child views are flipping.
251 */
252 public boolean isFlipping() {
253 return mStarted;
254 }
255
256 /**
257 * Set if this view automatically calls {@link #startFlipping()} when it
258 * becomes attached to a window.
259 */
260 public void setAutoStart(boolean autoStart) {
261 mAutoStart = autoStart;
262 }
263
264 /**
265 * Returns true if this view automatically calls {@link #startFlipping()}
266 * when it becomes attached to a window.
267 */
268 public boolean isAutoStart() {
269 return mAutoStart;
270 }
271
272 private final int FLIP_MSG = 1;
273
274 private final Handler mHandler = new Handler() {
275 @Override
276 public void handleMessage(Message msg) {
277 if (msg.what == FLIP_MSG) {
278 if (mRunning) {
279 showNext();
Adam Cohen3db40672010-07-19 22:41:57 -0700280 }
281 }
282 }
283 };
Adam Cohena02fdf12010-11-03 13:27:40 -0700284
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800285 /**
286 * Called by an {@link android.appwidget.AppWidgetHost} to indicate that it will be
287 * automatically advancing the views of this {@link AdapterViewFlipper} by calling
288 * {@link AdapterViewFlipper#advance()} at some point in the future. This allows
289 * {@link AdapterViewFlipper} to prepare by no longer Advancing its children.
290 */
Adam Cohena02fdf12010-11-03 13:27:40 -0700291 @Override
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800292 public void fyiWillBeAdvancedByHostKThx() {
Adam Cohena02fdf12010-11-03 13:27:40 -0700293 mAdvancedByHost = true;
294 updateRunning(false);
295 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800296
297 @Override
298 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
299 super.onInitializeAccessibilityEvent(event);
300 event.setClassName(AdapterViewFlipper.class.getName());
301 }
302
303 @Override
304 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
305 super.onInitializeAccessibilityNodeInfo(info);
306 info.setClassName(AdapterViewFlipper.class.getName());
307 }
Adam Cohen3db40672010-07-19 22:41:57 -0700308}