blob: 18d74705773a170b77f2e89a06ea3206702defd0 [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;
Adam Cohen3db40672010-07-19 22:41:57 -070024import android.os.Message;
25import android.util.AttributeSet;
26import android.util.Log;
Adam Cohen2dd21972010-08-15 18:20:04 -070027import android.view.RemotableViewMethod;
Adam Cohen3db40672010-07-19 22:41:57 -070028import android.widget.RemoteViews.RemoteView;
29
30/**
31 * Simple {@link ViewAnimator} that will animate between two or more views
32 * that have been added to it. Only one child is shown at a time. If
33 * requested, can automatically flip between each child at a regular interval.
34 *
35 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
36 * @attr ref android.R.styleable#AdapterViewFlipper_autoStart
37 */
38@RemoteView
39public class AdapterViewFlipper extends AdapterViewAnimator {
40 private static final String TAG = "ViewFlipper";
41 private static final boolean LOGD = false;
42
43 private static final int DEFAULT_INTERVAL = 10000;
Adam Cohen3db40672010-07-19 22:41:57 -070044
45 private int mFlipInterval = DEFAULT_INTERVAL;
Adam Cohen3db40672010-07-19 22:41:57 -070046 private boolean mAutoStart = false;
47
48 private boolean mRunning = false;
49 private boolean mStarted = false;
50 private boolean mVisible = false;
51 private boolean mUserPresent = true;
Adam Cohena02fdf12010-11-03 13:27:40 -070052 private boolean mAdvancedByHost = false;
Adam Cohen3db40672010-07-19 22:41:57 -070053
54 public AdapterViewFlipper(Context context) {
55 super(context);
Adam Cohen3db40672010-07-19 22:41:57 -070056 }
57
58 public AdapterViewFlipper(Context context, AttributeSet attrs) {
Alan Viverette617feb92013-09-09 18:09:13 -070059 this(context, attrs, 0);
60 }
Adam Cohen3db40672010-07-19 22:41:57 -070061
Alan Viverette617feb92013-09-09 18:09:13 -070062 public AdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) {
63 this(context, attrs, defStyleAttr, 0);
64 }
65
66 public AdapterViewFlipper(
67 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
68 super(context, attrs, defStyleAttr, defStyleRes);
69
70 final TypedArray a = context.obtainStyledAttributes(attrs,
71 com.android.internal.R.styleable.AdapterViewFlipper, defStyleAttr, defStyleRes);
Adam Cohen3db40672010-07-19 22:41:57 -070072 mFlipInterval = a.getInt(
Adam Cohen472566022011-07-22 16:28:21 -070073 com.android.internal.R.styleable.AdapterViewFlipper_flipInterval, DEFAULT_INTERVAL);
Adam Cohen3db40672010-07-19 22:41:57 -070074 mAutoStart = a.getBoolean(
Adam Cohen472566022011-07-22 16:28:21 -070075 com.android.internal.R.styleable.AdapterViewFlipper_autoStart, false);
Adam Cohen1b065cd2010-09-28 14:53:47 -070076
Adam Cohen472566022011-07-22 16:28:21 -070077 // A view flipper should cycle through the views
78 mLoopViews = true;
Adam Cohen1b065cd2010-09-28 14:53:47 -070079
Adam Cohen3db40672010-07-19 22:41:57 -070080 a.recycle();
Adam Cohen3db40672010-07-19 22:41:57 -070081 }
82
83 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
84 @Override
85 public void onReceive(Context context, Intent intent) {
86 final String action = intent.getAction();
87 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
88 mUserPresent = false;
89 updateRunning();
90 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
91 mUserPresent = true;
92 updateRunning(false);
93 }
94 }
95 };
96
97 @Override
98 protected void onAttachedToWindow() {
99 super.onAttachedToWindow();
100
101 // Listen for broadcasts related to user-presence
102 final IntentFilter filter = new IntentFilter();
103 filter.addAction(Intent.ACTION_SCREEN_OFF);
104 filter.addAction(Intent.ACTION_USER_PRESENT);
Svetoslav985e5662014-08-06 22:05:48 -0700105
106 // OK, this is gross but needed. This class is supported by the
107 // remote views machanism and as a part of that the remote views
108 // can be inflated by a context for another user without the app
109 // having interact users permission - just for loading resources.
110 // For exmaple, when adding widgets from a user profile to the
111 // home screen. Therefore, we register the receiver as the current
112 // user not the one the context is for.
113 getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
John Reckd0374c62015-10-20 13:25:01 -0700114 filter, null, getHandler());
Svetoslav985e5662014-08-06 22:05:48 -0700115
Adam Cohen3db40672010-07-19 22:41:57 -0700116
117 if (mAutoStart) {
118 // Automatically start when requested
119 startFlipping();
120 }
121 }
122
123 @Override
124 protected void onDetachedFromWindow() {
125 super.onDetachedFromWindow();
126 mVisible = false;
127
128 getContext().unregisterReceiver(mReceiver);
129 updateRunning();
130 }
131
132 @Override
133 protected void onWindowVisibilityChanged(int visibility) {
134 super.onWindowVisibilityChanged(visibility);
135 mVisible = (visibility == VISIBLE);
136 updateRunning(false);
137 }
138
139 @Override
140 public void setAdapter(Adapter adapter) {
141 super.setAdapter(adapter);
142 updateRunning();
143 }
144
145 /**
Philip Milneaac722a2012-03-26 13:30:26 -0700146 * Returns the flip interval, in milliseconds.
Adam Cohen3db40672010-07-19 22:41:57 -0700147 *
Philip Milneaac722a2012-03-26 13:30:26 -0700148 * @return the flip interval in milliseconds
149 *
150 * @see #setFlipInterval(int)
151 *
152 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
Adam Cohen3db40672010-07-19 22:41:57 -0700153 */
Philip Milneaac722a2012-03-26 13:30:26 -0700154 public int getFlipInterval() {
155 return mFlipInterval;
156 }
157
158 /**
159 * How long to wait before flipping to the next view.
160 *
161 * @param flipInterval flip interval in milliseconds
162 *
163 * @see #getFlipInterval()
164 *
165 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
166 */
167 public void setFlipInterval(int flipInterval) {
168 mFlipInterval = flipInterval;
Adam Cohen3db40672010-07-19 22:41:57 -0700169 }
170
171 /**
172 * Start a timer to cycle through child views
173 */
174 public void startFlipping() {
175 mStarted = true;
176 updateRunning();
177 }
178
179 /**
180 * No more flips
181 */
182 public void stopFlipping() {
183 mStarted = false;
184 updateRunning();
185 }
186
187 /**
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700188 * {@inheritDoc}
189 */
190 @Override
191 @RemotableViewMethod
192 public void showNext() {
193 // if the flipper is currently flipping automatically, and showNext() is called
194 // we should we should make sure to reset the timer
195 if (mRunning) {
John Reckd0374c62015-10-20 13:25:01 -0700196 removeCallbacks(mFlipRunnable);
197 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700198 }
199 super.showNext();
200 }
201
202 /**
203 * {@inheritDoc}
204 */
205 @Override
206 @RemotableViewMethod
207 public void showPrevious() {
208 // if the flipper is currently flipping automatically, and showPrevious() is called
209 // we should we should make sure to reset the timer
210 if (mRunning) {
John Reckd0374c62015-10-20 13:25:01 -0700211 removeCallbacks(mFlipRunnable);
212 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700213 }
214 super.showPrevious();
215 }
216
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700217 /**
Adam Cohen3db40672010-07-19 22:41:57 -0700218 * Internal method to start or stop dispatching flip {@link Message} based
219 * on {@link #mRunning} and {@link #mVisible} state.
220 */
221 private void updateRunning() {
222 // by default when we update running, we want the
223 // current view to animate in
224 updateRunning(true);
225 }
226
227 /**
228 * Internal method to start or stop dispatching flip {@link Message} based
229 * on {@link #mRunning} and {@link #mVisible} state.
230 *
231 * @param flipNow Determines whether or not to execute the animation now, in
232 * addition to queuing future flips. If omitted, defaults to
233 * true.
234 */
235 private void updateRunning(boolean flipNow) {
Adam Cohena02fdf12010-11-03 13:27:40 -0700236 boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
237 && mAdapter != null;
Adam Cohen3db40672010-07-19 22:41:57 -0700238 if (running != mRunning) {
239 if (running) {
240 showOnly(mWhichChild, flipNow);
John Reckd0374c62015-10-20 13:25:01 -0700241 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohen3db40672010-07-19 22:41:57 -0700242 } else {
John Reckd0374c62015-10-20 13:25:01 -0700243 removeCallbacks(mFlipRunnable);
Adam Cohen3db40672010-07-19 22:41:57 -0700244 }
245 mRunning = running;
246 }
247 if (LOGD) {
248 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
249 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
250 }
251 }
252
253 /**
254 * Returns true if the child views are flipping.
255 */
256 public boolean isFlipping() {
257 return mStarted;
258 }
259
260 /**
261 * Set if this view automatically calls {@link #startFlipping()} when it
262 * becomes attached to a window.
263 */
264 public void setAutoStart(boolean autoStart) {
265 mAutoStart = autoStart;
266 }
267
268 /**
269 * Returns true if this view automatically calls {@link #startFlipping()}
270 * when it becomes attached to a window.
271 */
272 public boolean isAutoStart() {
273 return mAutoStart;
274 }
275
John Reckd0374c62015-10-20 13:25:01 -0700276 private final Runnable mFlipRunnable = new Runnable() {
Adam Cohen3db40672010-07-19 22:41:57 -0700277 @Override
John Reckd0374c62015-10-20 13:25:01 -0700278 public void run() {
279 if (mRunning) {
280 showNext();
Adam Cohen3db40672010-07-19 22:41:57 -0700281 }
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
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800298 public CharSequence getAccessibilityClassName() {
299 return AdapterViewFlipper.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800300 }
Adam Cohen3db40672010-07-19 22:41:57 -0700301}