1 /*
2  * Copyright (C) 2006 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 
17 package android.widget;
18 
19 import android.annotation.IntRange;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.content.Context;
22 import android.content.res.TypedArray;
23 import android.os.Build;
24 import android.os.Message;
25 import android.util.AttributeSet;
26 import android.util.Log;
27 import android.view.inspector.InspectableProperty;
28 import 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#ViewFlipper_flipInterval
36  * @attr ref android.R.styleable#ViewFlipper_autoStart
37  */
38 @RemoteView
39 public class ViewFlipper extends ViewAnimator {
40     private static final String TAG = "ViewFlipper";
41     private static final boolean LOGD = false;
42 
43     private static final int DEFAULT_INTERVAL = 3000;
44 
45     private int mFlipInterval = DEFAULT_INTERVAL;
46     private boolean mAutoStart = false;
47 
48     private boolean mRunning = false;
49     private boolean mStarted = false;
50     private boolean mVisible = false;
51 
ViewFlipper(Context context)52     public ViewFlipper(Context context) {
53         super(context);
54     }
55 
ViewFlipper(Context context, AttributeSet attrs)56     public ViewFlipper(Context context, AttributeSet attrs) {
57         super(context, attrs);
58 
59         TypedArray a = context.obtainStyledAttributes(attrs,
60                 com.android.internal.R.styleable.ViewFlipper);
61         mFlipInterval = a.getInt(
62                 com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL);
63         mAutoStart = a.getBoolean(
64                 com.android.internal.R.styleable.ViewFlipper_autoStart, false);
65         a.recycle();
66     }
67 
68     @Override
onAttachedToWindow()69     protected void onAttachedToWindow() {
70         super.onAttachedToWindow();
71 
72         if (mAutoStart) {
73             // Automatically start when requested
74             startFlipping();
75         }
76     }
77 
78     @Override
onDetachedFromWindow()79     protected void onDetachedFromWindow() {
80         super.onDetachedFromWindow();
81         mVisible = false;
82         updateRunning();
83     }
84 
85     @Override
onWindowVisibilityChanged(int visibility)86     protected void onWindowVisibilityChanged(int visibility) {
87         super.onWindowVisibilityChanged(visibility);
88         mVisible = visibility == VISIBLE;
89         updateRunning(false);
90     }
91 
92     /**
93      * How long to wait before flipping to the next view
94      *
95      * @param milliseconds
96      *            time in milliseconds
97      */
98     @android.view.RemotableViewMethod
setFlipInterval(@ntRangefrom = 0) int milliseconds)99     public void setFlipInterval(@IntRange(from = 0) int milliseconds) {
100         mFlipInterval = milliseconds;
101     }
102 
103     /**
104      * Get the delay before flipping to the next view.
105      *
106      * @return delay time in milliseconds
107      */
108     @InspectableProperty
109     @IntRange(from = 0)
getFlipInterval()110     public int getFlipInterval() {
111         return mFlipInterval;
112     }
113 
114     /**
115      * Start a timer to cycle through child views
116      */
startFlipping()117     public void startFlipping() {
118         mStarted = true;
119         updateRunning();
120     }
121 
122     /**
123      * No more flips
124      */
stopFlipping()125     public void stopFlipping() {
126         mStarted = false;
127         updateRunning();
128     }
129 
130     @Override
getAccessibilityClassName()131     public CharSequence getAccessibilityClassName() {
132         return ViewFlipper.class.getName();
133     }
134 
135     /**
136      * Internal method to start or stop dispatching flip {@link Message} based
137      * on {@link #mRunning} and {@link #mVisible} state.
138      */
updateRunning()139     private void updateRunning() {
140         updateRunning(true);
141     }
142 
143     /**
144      * Internal method to start or stop dispatching flip {@link Message} based
145      * on {@link #mRunning} and {@link #mVisible} state.
146      *
147      * @param flipNow Determines whether or not to execute the animation now, in
148      *            addition to queuing future flips. If omitted, defaults to
149      *            true.
150      */
151     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
updateRunning(boolean flipNow)152     private void updateRunning(boolean flipNow) {
153         boolean running = mVisible && mStarted;
154         if (running != mRunning) {
155             if (running) {
156                 showOnly(mWhichChild, flipNow);
157                 postDelayed(mFlipRunnable, mFlipInterval);
158             } else {
159                 removeCallbacks(mFlipRunnable);
160             }
161             mRunning = running;
162         }
163         if (LOGD) {
164             Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
165                     + ", mRunning=" + mRunning);
166         }
167     }
168 
169     /**
170      * Returns true if the child views are flipping.
171      */
172     @InspectableProperty(hasAttributeId = false)
isFlipping()173     public boolean isFlipping() {
174         return mStarted;
175     }
176 
177     /**
178      * Set if this view automatically calls {@link #startFlipping()} when it
179      * becomes attached to a window.
180      */
setAutoStart(boolean autoStart)181     public void setAutoStart(boolean autoStart) {
182         mAutoStart = autoStart;
183     }
184 
185     /**
186      * Returns true if this view automatically calls {@link #startFlipping()}
187      * when it becomes attached to a window.
188      */
189     @InspectableProperty
isAutoStart()190     public boolean isAutoStart() {
191         return mAutoStart;
192     }
193 
194     private final Runnable mFlipRunnable = new Runnable() {
195         @Override
196         public void run() {
197             if (mRunning) {
198                 showNext();
199                 postDelayed(mFlipRunnable, mFlipInterval);
200             }
201         }
202     };
203 }
204