blob: 285dee8e368a3ea5cadf5ddda85a42baa10d060c [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);
Svetoslav985e5662014-08-06 22:05:48 -0700108
109 // OK, this is gross but needed. This class is supported by the
110 // remote views machanism and as a part of that the remote views
111 // can be inflated by a context for another user without the app
112 // having interact users permission - just for loading resources.
113 // For exmaple, when adding widgets from a user profile to the
114 // home screen. Therefore, we register the receiver as the current
115 // user not the one the context is for.
116 getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
117 filter, null, mHandler);
118
Adam Cohen3db40672010-07-19 22:41:57 -0700119
120 if (mAutoStart) {
121 // Automatically start when requested
122 startFlipping();
123 }
124 }
125
126 @Override
127 protected void onDetachedFromWindow() {
128 super.onDetachedFromWindow();
129 mVisible = false;
130
131 getContext().unregisterReceiver(mReceiver);
132 updateRunning();
133 }
134
135 @Override
136 protected void onWindowVisibilityChanged(int visibility) {
137 super.onWindowVisibilityChanged(visibility);
138 mVisible = (visibility == VISIBLE);
139 updateRunning(false);
140 }
141
142 @Override
143 public void setAdapter(Adapter adapter) {
144 super.setAdapter(adapter);
145 updateRunning();
146 }
147
148 /**
Philip Milneaac722a2012-03-26 13:30:26 -0700149 * Returns the flip interval, in milliseconds.
Adam Cohen3db40672010-07-19 22:41:57 -0700150 *
Philip Milneaac722a2012-03-26 13:30:26 -0700151 * @return the flip interval in milliseconds
152 *
153 * @see #setFlipInterval(int)
154 *
155 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
Adam Cohen3db40672010-07-19 22:41:57 -0700156 */
Philip Milneaac722a2012-03-26 13:30:26 -0700157 public int getFlipInterval() {
158 return mFlipInterval;
159 }
160
161 /**
162 * How long to wait before flipping to the next view.
163 *
164 * @param flipInterval flip interval in milliseconds
165 *
166 * @see #getFlipInterval()
167 *
168 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
169 */
170 public void setFlipInterval(int flipInterval) {
171 mFlipInterval = flipInterval;
Adam Cohen3db40672010-07-19 22:41:57 -0700172 }
173
174 /**
175 * Start a timer to cycle through child views
176 */
177 public void startFlipping() {
178 mStarted = true;
179 updateRunning();
180 }
181
182 /**
183 * No more flips
184 */
185 public void stopFlipping() {
186 mStarted = false;
187 updateRunning();
188 }
189
190 /**
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700191 * {@inheritDoc}
192 */
193 @Override
194 @RemotableViewMethod
195 public void showNext() {
196 // if the flipper is currently flipping automatically, and showNext() is called
197 // we should we should make sure to reset the timer
198 if (mRunning) {
199 mHandler.removeMessages(FLIP_MSG);
200 Message msg = mHandler.obtainMessage(FLIP_MSG);
201 mHandler.sendMessageDelayed(msg, mFlipInterval);
202 }
203 super.showNext();
204 }
205
206 /**
207 * {@inheritDoc}
208 */
209 @Override
210 @RemotableViewMethod
211 public void showPrevious() {
212 // if the flipper is currently flipping automatically, and showPrevious() is called
213 // we should we should make sure to reset the timer
214 if (mRunning) {
215 mHandler.removeMessages(FLIP_MSG);
216 Message msg = mHandler.obtainMessage(FLIP_MSG);
217 mHandler.sendMessageDelayed(msg, mFlipInterval);
218 }
219 super.showPrevious();
220 }
221
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700222 /**
Adam Cohen3db40672010-07-19 22:41:57 -0700223 * Internal method to start or stop dispatching flip {@link Message} based
224 * on {@link #mRunning} and {@link #mVisible} state.
225 */
226 private void updateRunning() {
227 // by default when we update running, we want the
228 // current view to animate in
229 updateRunning(true);
230 }
231
232 /**
233 * Internal method to start or stop dispatching flip {@link Message} based
234 * on {@link #mRunning} and {@link #mVisible} state.
235 *
236 * @param flipNow Determines whether or not to execute the animation now, in
237 * addition to queuing future flips. If omitted, defaults to
238 * true.
239 */
240 private void updateRunning(boolean flipNow) {
Adam Cohena02fdf12010-11-03 13:27:40 -0700241 boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
242 && mAdapter != null;
Adam Cohen3db40672010-07-19 22:41:57 -0700243 if (running != mRunning) {
244 if (running) {
245 showOnly(mWhichChild, flipNow);
246 Message msg = mHandler.obtainMessage(FLIP_MSG);
247 mHandler.sendMessageDelayed(msg, mFlipInterval);
248 } else {
249 mHandler.removeMessages(FLIP_MSG);
250 }
251 mRunning = running;
252 }
253 if (LOGD) {
254 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
255 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
256 }
257 }
258
259 /**
260 * Returns true if the child views are flipping.
261 */
262 public boolean isFlipping() {
263 return mStarted;
264 }
265
266 /**
267 * Set if this view automatically calls {@link #startFlipping()} when it
268 * becomes attached to a window.
269 */
270 public void setAutoStart(boolean autoStart) {
271 mAutoStart = autoStart;
272 }
273
274 /**
275 * Returns true if this view automatically calls {@link #startFlipping()}
276 * when it becomes attached to a window.
277 */
278 public boolean isAutoStart() {
279 return mAutoStart;
280 }
281
282 private final int FLIP_MSG = 1;
283
284 private final Handler mHandler = new Handler() {
285 @Override
286 public void handleMessage(Message msg) {
287 if (msg.what == FLIP_MSG) {
288 if (mRunning) {
289 showNext();
Adam Cohen3db40672010-07-19 22:41:57 -0700290 }
291 }
292 }
293 };
Adam Cohena02fdf12010-11-03 13:27:40 -0700294
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800295 /**
296 * Called by an {@link android.appwidget.AppWidgetHost} to indicate that it will be
297 * automatically advancing the views of this {@link AdapterViewFlipper} by calling
298 * {@link AdapterViewFlipper#advance()} at some point in the future. This allows
299 * {@link AdapterViewFlipper} to prepare by no longer Advancing its children.
300 */
Adam Cohena02fdf12010-11-03 13:27:40 -0700301 @Override
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800302 public void fyiWillBeAdvancedByHostKThx() {
Adam Cohena02fdf12010-11-03 13:27:40 -0700303 mAdvancedByHost = true;
304 updateRunning(false);
305 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800306
307 @Override
308 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
309 super.onInitializeAccessibilityEvent(event);
310 event.setClassName(AdapterViewFlipper.class.getName());
311 }
312
313 @Override
314 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
315 super.onInitializeAccessibilityNodeInfo(info);
316 info.setClassName(AdapterViewFlipper.class.getName());
317 }
Adam Cohen3db40672010-07-19 22:41:57 -0700318}