blob: 065089f5363304274d30f1281db01263f51c863c [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);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -080072 saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AdapterViewFlipper,
73 attrs, a, defStyleAttr, defStyleRes);
Adam Cohen3db40672010-07-19 22:41:57 -070074 mFlipInterval = a.getInt(
Adam Cohen472566022011-07-22 16:28:21 -070075 com.android.internal.R.styleable.AdapterViewFlipper_flipInterval, DEFAULT_INTERVAL);
Adam Cohen3db40672010-07-19 22:41:57 -070076 mAutoStart = a.getBoolean(
Adam Cohen472566022011-07-22 16:28:21 -070077 com.android.internal.R.styleable.AdapterViewFlipper_autoStart, false);
Adam Cohen1b065cd2010-09-28 14:53:47 -070078
Adam Cohen472566022011-07-22 16:28:21 -070079 // A view flipper should cycle through the views
80 mLoopViews = true;
Adam Cohen1b065cd2010-09-28 14:53:47 -070081
Adam Cohen3db40672010-07-19 22:41:57 -070082 a.recycle();
Adam Cohen3db40672010-07-19 22:41:57 -070083 }
84
85 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
86 @Override
87 public void onReceive(Context context, Intent intent) {
88 final String action = intent.getAction();
89 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
90 mUserPresent = false;
91 updateRunning();
92 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
93 mUserPresent = true;
94 updateRunning(false);
95 }
96 }
97 };
98
99 @Override
100 protected void onAttachedToWindow() {
101 super.onAttachedToWindow();
102
103 // Listen for broadcasts related to user-presence
104 final IntentFilter filter = new IntentFilter();
105 filter.addAction(Intent.ACTION_SCREEN_OFF);
106 filter.addAction(Intent.ACTION_USER_PRESENT);
Svetoslav985e5662014-08-06 22:05:48 -0700107
108 // OK, this is gross but needed. This class is supported by the
109 // remote views machanism and as a part of that the remote views
110 // can be inflated by a context for another user without the app
111 // having interact users permission - just for loading resources.
112 // For exmaple, when adding widgets from a user profile to the
113 // home screen. Therefore, we register the receiver as the current
114 // user not the one the context is for.
115 getContext().registerReceiverAsUser(mReceiver, android.os.Process.myUserHandle(),
John Reckd0374c62015-10-20 13:25:01 -0700116 filter, null, getHandler());
Svetoslav985e5662014-08-06 22:05:48 -0700117
Adam Cohen3db40672010-07-19 22:41:57 -0700118
119 if (mAutoStart) {
120 // Automatically start when requested
121 startFlipping();
122 }
123 }
124
125 @Override
126 protected void onDetachedFromWindow() {
127 super.onDetachedFromWindow();
128 mVisible = false;
129
130 getContext().unregisterReceiver(mReceiver);
131 updateRunning();
132 }
133
134 @Override
135 protected void onWindowVisibilityChanged(int visibility) {
136 super.onWindowVisibilityChanged(visibility);
137 mVisible = (visibility == VISIBLE);
138 updateRunning(false);
139 }
140
141 @Override
142 public void setAdapter(Adapter adapter) {
143 super.setAdapter(adapter);
144 updateRunning();
145 }
146
147 /**
Philip Milneaac722a2012-03-26 13:30:26 -0700148 * Returns the flip interval, in milliseconds.
Adam Cohen3db40672010-07-19 22:41:57 -0700149 *
Philip Milneaac722a2012-03-26 13:30:26 -0700150 * @return the flip interval in milliseconds
151 *
152 * @see #setFlipInterval(int)
153 *
154 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
Adam Cohen3db40672010-07-19 22:41:57 -0700155 */
Philip Milneaac722a2012-03-26 13:30:26 -0700156 public int getFlipInterval() {
157 return mFlipInterval;
158 }
159
160 /**
161 * How long to wait before flipping to the next view.
162 *
163 * @param flipInterval flip interval in milliseconds
164 *
165 * @see #getFlipInterval()
166 *
167 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval
168 */
169 public void setFlipInterval(int flipInterval) {
170 mFlipInterval = flipInterval;
Adam Cohen3db40672010-07-19 22:41:57 -0700171 }
172
173 /**
174 * Start a timer to cycle through child views
175 */
176 public void startFlipping() {
177 mStarted = true;
178 updateRunning();
179 }
180
181 /**
182 * No more flips
183 */
184 public void stopFlipping() {
185 mStarted = false;
186 updateRunning();
187 }
188
189 /**
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700190 * {@inheritDoc}
191 */
192 @Override
193 @RemotableViewMethod
194 public void showNext() {
195 // if the flipper is currently flipping automatically, and showNext() is called
196 // we should we should make sure to reset the timer
197 if (mRunning) {
John Reckd0374c62015-10-20 13:25:01 -0700198 removeCallbacks(mFlipRunnable);
199 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700200 }
201 super.showNext();
202 }
203
204 /**
205 * {@inheritDoc}
206 */
207 @Override
208 @RemotableViewMethod
209 public void showPrevious() {
210 // if the flipper is currently flipping automatically, and showPrevious() is called
211 // we should we should make sure to reset the timer
212 if (mRunning) {
John Reckd0374c62015-10-20 13:25:01 -0700213 removeCallbacks(mFlipRunnable);
214 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700215 }
216 super.showPrevious();
217 }
218
Adam Cohenb04f7ad2010-08-15 13:22:42 -0700219 /**
Adam Cohen3db40672010-07-19 22:41:57 -0700220 * Internal method to start or stop dispatching flip {@link Message} based
221 * on {@link #mRunning} and {@link #mVisible} state.
222 */
223 private void updateRunning() {
224 // by default when we update running, we want the
225 // current view to animate in
226 updateRunning(true);
227 }
228
229 /**
230 * Internal method to start or stop dispatching flip {@link Message} based
231 * on {@link #mRunning} and {@link #mVisible} state.
232 *
233 * @param flipNow Determines whether or not to execute the animation now, in
234 * addition to queuing future flips. If omitted, defaults to
235 * true.
236 */
237 private void updateRunning(boolean flipNow) {
Adam Cohena02fdf12010-11-03 13:27:40 -0700238 boolean running = !mAdvancedByHost && mVisible && mStarted && mUserPresent
239 && mAdapter != null;
Adam Cohen3db40672010-07-19 22:41:57 -0700240 if (running != mRunning) {
241 if (running) {
242 showOnly(mWhichChild, flipNow);
John Reckd0374c62015-10-20 13:25:01 -0700243 postDelayed(mFlipRunnable, mFlipInterval);
Adam Cohen3db40672010-07-19 22:41:57 -0700244 } else {
John Reckd0374c62015-10-20 13:25:01 -0700245 removeCallbacks(mFlipRunnable);
Adam Cohen3db40672010-07-19 22:41:57 -0700246 }
247 mRunning = running;
248 }
249 if (LOGD) {
250 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
251 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
252 }
253 }
254
255 /**
256 * Returns true if the child views are flipping.
257 */
258 public boolean isFlipping() {
259 return mStarted;
260 }
261
262 /**
263 * Set if this view automatically calls {@link #startFlipping()} when it
264 * becomes attached to a window.
265 */
266 public void setAutoStart(boolean autoStart) {
267 mAutoStart = autoStart;
268 }
269
270 /**
271 * Returns true if this view automatically calls {@link #startFlipping()}
272 * when it becomes attached to a window.
273 */
274 public boolean isAutoStart() {
275 return mAutoStart;
276 }
277
John Reckd0374c62015-10-20 13:25:01 -0700278 private final Runnable mFlipRunnable = new Runnable() {
Adam Cohen3db40672010-07-19 22:41:57 -0700279 @Override
John Reckd0374c62015-10-20 13:25:01 -0700280 public void run() {
281 if (mRunning) {
282 showNext();
Adam Cohen3db40672010-07-19 22:41:57 -0700283 }
284 }
285 };
Adam Cohena02fdf12010-11-03 13:27:40 -0700286
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800287 /**
288 * Called by an {@link android.appwidget.AppWidgetHost} to indicate that it will be
289 * automatically advancing the views of this {@link AdapterViewFlipper} by calling
290 * {@link AdapterViewFlipper#advance()} at some point in the future. This allows
291 * {@link AdapterViewFlipper} to prepare by no longer Advancing its children.
292 */
Adam Cohena02fdf12010-11-03 13:27:40 -0700293 @Override
Adam Cohen0e2de6d2011-01-19 17:16:34 -0800294 public void fyiWillBeAdvancedByHostKThx() {
Adam Cohena02fdf12010-11-03 13:27:40 -0700295 mAdvancedByHost = true;
296 updateRunning(false);
297 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800298
299 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800300 public CharSequence getAccessibilityClassName() {
301 return AdapterViewFlipper.class.getName();
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800302 }
Adam Cohen3db40672010-07-19 22:41:57 -0700303}