AdapterViewFlipper.java revision ef52176f1244a5bb98d82a0c8c7f4351edec17a1
1/*
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.animation.ObjectAnimator;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.res.TypedArray;
25import android.os.Handler;
26import android.os.Message;
27import android.util.AttributeSet;
28import android.util.Log;
29import android.view.RemotableViewMethod;
30import android.view.animation.AlphaAnimation;
31import 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;
47
48    private int mFlipInterval = DEFAULT_INTERVAL;
49    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;
55
56    public AdapterViewFlipper(Context context) {
57        super(context);
58    }
59
60    public AdapterViewFlipper(Context context, AttributeSet attrs) {
61        super(context, attrs);
62
63        TypedArray a = context.obtainStyledAttributes(attrs,
64                com.android.internal.R.styleable.ViewFlipper);
65        mFlipInterval = a.getInt(
66                com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL);
67        mAutoStart = a.getBoolean(
68                com.android.internal.R.styleable.ViewFlipper_autoStart, false);
69
70        // By default we want the flipper to loop
71        mLoopViews = a.getBoolean(
72                com.android.internal.R.styleable.AdapterViewAnimator_loopViews, true);
73
74        a.recycle();
75    }
76
77    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
78        @Override
79        public void onReceive(Context context, Intent intent) {
80            final String action = intent.getAction();
81            if (Intent.ACTION_SCREEN_OFF.equals(action)) {
82                mUserPresent = false;
83                updateRunning();
84            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
85                mUserPresent = true;
86                updateRunning(false);
87            }
88        }
89    };
90
91    @Override
92    protected void onAttachedToWindow() {
93        super.onAttachedToWindow();
94
95        // Listen for broadcasts related to user-presence
96        final IntentFilter filter = new IntentFilter();
97        filter.addAction(Intent.ACTION_SCREEN_OFF);
98        filter.addAction(Intent.ACTION_USER_PRESENT);
99        getContext().registerReceiver(mReceiver, filter);
100
101        if (mAutoStart) {
102            // Automatically start when requested
103            startFlipping();
104        }
105    }
106
107    @Override
108    protected void onDetachedFromWindow() {
109        super.onDetachedFromWindow();
110        mVisible = false;
111
112        getContext().unregisterReceiver(mReceiver);
113        updateRunning();
114    }
115
116    @Override
117    protected void onWindowVisibilityChanged(int visibility) {
118        super.onWindowVisibilityChanged(visibility);
119        mVisible = (visibility == VISIBLE);
120        updateRunning(false);
121    }
122
123    @Override
124    public void setAdapter(Adapter adapter) {
125        super.setAdapter(adapter);
126        updateRunning();
127    }
128
129    /**
130     * How long to wait before flipping to the next view
131     *
132     * @param milliseconds
133     *            time in milliseconds
134     */
135    public void setFlipInterval(int milliseconds) {
136        mFlipInterval = milliseconds;
137    }
138
139    /**
140     * Start a timer to cycle through child views
141     */
142    public void startFlipping() {
143        mStarted = true;
144        updateRunning();
145    }
146
147    /**
148     * No more flips
149     */
150    public void stopFlipping() {
151        mStarted = false;
152        updateRunning();
153    }
154
155    /**
156    * {@inheritDoc}
157    */
158   @Override
159   @RemotableViewMethod
160   public void showNext() {
161       // if the flipper is currently flipping automatically, and showNext() is called
162       // we should we should make sure to reset the timer
163       if (mRunning) {
164           mHandler.removeMessages(FLIP_MSG);
165           Message msg = mHandler.obtainMessage(FLIP_MSG);
166           mHandler.sendMessageDelayed(msg, mFlipInterval);
167       }
168       super.showNext();
169   }
170
171   /**
172    * {@inheritDoc}
173    */
174   @Override
175   @RemotableViewMethod
176   public void showPrevious() {
177       // if the flipper is currently flipping automatically, and showPrevious() is called
178       // we should we should make sure to reset the timer
179       if (mRunning) {
180           mHandler.removeMessages(FLIP_MSG);
181           Message msg = mHandler.obtainMessage(FLIP_MSG);
182           mHandler.sendMessageDelayed(msg, mFlipInterval);
183       }
184       super.showPrevious();
185   }
186
187    /**
188     * Internal method to start or stop dispatching flip {@link Message} based
189     * on {@link #mRunning} and {@link #mVisible} state.
190     */
191    private void updateRunning() {
192        // by default when we update running, we want the
193        // current view to animate in
194        updateRunning(true);
195    }
196
197    /**
198     * Internal method to start or stop dispatching flip {@link Message} based
199     * on {@link #mRunning} and {@link #mVisible} state.
200     *
201     * @param flipNow Determines whether or not to execute the animation now, in
202     *            addition to queuing future flips. If omitted, defaults to
203     *            true.
204     */
205    private void updateRunning(boolean flipNow) {
206        boolean running = mVisible && mStarted && mUserPresent && mAdapter != null;
207        if (running != mRunning) {
208            if (running) {
209                showOnly(mWhichChild, flipNow);
210                Message msg = mHandler.obtainMessage(FLIP_MSG);
211                mHandler.sendMessageDelayed(msg, mFlipInterval);
212            } else {
213                mHandler.removeMessages(FLIP_MSG);
214            }
215            mRunning = running;
216        }
217        if (LOGD) {
218            Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
219                    + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
220        }
221    }
222
223    /**
224     * Returns true if the child views are flipping.
225     */
226    public boolean isFlipping() {
227        return mStarted;
228    }
229
230    /**
231     * Set if this view automatically calls {@link #startFlipping()} when it
232     * becomes attached to a window.
233     */
234    public void setAutoStart(boolean autoStart) {
235        mAutoStart = autoStart;
236    }
237
238    /**
239     * Returns true if this view automatically calls {@link #startFlipping()}
240     * when it becomes attached to a window.
241     */
242    public boolean isAutoStart() {
243        return mAutoStart;
244    }
245
246    private final int FLIP_MSG = 1;
247
248    private final Handler mHandler = new Handler() {
249        @Override
250        public void handleMessage(Message msg) {
251            if (msg.what == FLIP_MSG) {
252                if (mRunning) {
253                    showNext();
254                }
255            }
256        }
257    };
258}
259