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 17 package android.widget; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.os.Message; 22 import android.util.AttributeSet; 23 import android.util.Log; 24 import android.view.RemotableViewMethod; 25 import android.widget.RemoteViews.RemoteView; 26 27 /** 28 * Simple {@link ViewAnimator} that will animate between two or more views 29 * that have been added to it. Only one child is shown at a time. If 30 * requested, can automatically flip between each child at a regular interval. 31 * 32 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval 33 * @attr ref android.R.styleable#AdapterViewFlipper_autoStart 34 */ 35 @RemoteView 36 public class AdapterViewFlipper extends AdapterViewAnimator { 37 private static final String TAG = "ViewFlipper"; 38 private static final boolean LOGD = false; 39 40 private static final int DEFAULT_INTERVAL = 10000; 41 42 private int mFlipInterval = DEFAULT_INTERVAL; 43 private boolean mAutoStart = false; 44 45 private boolean mRunning = false; 46 private boolean mStarted = false; 47 private boolean mVisible = false; 48 private boolean mAdvancedByHost = false; 49 AdapterViewFlipper(Context context)50 public AdapterViewFlipper(Context context) { 51 super(context); 52 } 53 AdapterViewFlipper(Context context, AttributeSet attrs)54 public AdapterViewFlipper(Context context, AttributeSet attrs) { 55 this(context, attrs, 0); 56 } 57 AdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr)58 public AdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) { 59 this(context, attrs, defStyleAttr, 0); 60 } 61 AdapterViewFlipper( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)62 public AdapterViewFlipper( 63 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 64 super(context, attrs, defStyleAttr, defStyleRes); 65 66 final TypedArray a = context.obtainStyledAttributes(attrs, 67 com.android.internal.R.styleable.AdapterViewFlipper, defStyleAttr, defStyleRes); 68 saveAttributeDataForStyleable(context, com.android.internal.R.styleable.AdapterViewFlipper, 69 attrs, a, defStyleAttr, defStyleRes); 70 mFlipInterval = a.getInt( 71 com.android.internal.R.styleable.AdapterViewFlipper_flipInterval, DEFAULT_INTERVAL); 72 mAutoStart = a.getBoolean( 73 com.android.internal.R.styleable.AdapterViewFlipper_autoStart, false); 74 75 // A view flipper should cycle through the views 76 mLoopViews = true; 77 78 a.recycle(); 79 } 80 81 @Override onAttachedToWindow()82 protected void onAttachedToWindow() { 83 super.onAttachedToWindow(); 84 85 if (mAutoStart) { 86 // Automatically start when requested 87 startFlipping(); 88 } 89 } 90 91 @Override onDetachedFromWindow()92 protected void onDetachedFromWindow() { 93 super.onDetachedFromWindow(); 94 mVisible = false; 95 updateRunning(); 96 } 97 98 @Override onWindowVisibilityChanged(int visibility)99 protected void onWindowVisibilityChanged(int visibility) { 100 super.onWindowVisibilityChanged(visibility); 101 mVisible = (visibility == VISIBLE); 102 updateRunning(false); 103 } 104 105 @Override setAdapter(Adapter adapter)106 public void setAdapter(Adapter adapter) { 107 super.setAdapter(adapter); 108 updateRunning(); 109 } 110 111 /** 112 * Returns the flip interval, in milliseconds. 113 * 114 * @return the flip interval in milliseconds 115 * 116 * @see #setFlipInterval(int) 117 * 118 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval 119 */ getFlipInterval()120 public int getFlipInterval() { 121 return mFlipInterval; 122 } 123 124 /** 125 * How long to wait before flipping to the next view. 126 * 127 * @param flipInterval flip interval in milliseconds 128 * 129 * @see #getFlipInterval() 130 * 131 * @attr ref android.R.styleable#AdapterViewFlipper_flipInterval 132 */ setFlipInterval(int flipInterval)133 public void setFlipInterval(int flipInterval) { 134 mFlipInterval = flipInterval; 135 } 136 137 /** 138 * Start a timer to cycle through child views 139 */ startFlipping()140 public void startFlipping() { 141 mStarted = true; 142 updateRunning(); 143 } 144 145 /** 146 * No more flips 147 */ stopFlipping()148 public void stopFlipping() { 149 mStarted = false; 150 updateRunning(); 151 } 152 153 /** 154 * {@inheritDoc} 155 */ 156 @Override 157 @RemotableViewMethod showNext()158 public void showNext() { 159 // if the flipper is currently flipping automatically, and showNext() is called 160 // we should we should make sure to reset the timer 161 if (mRunning) { 162 removeCallbacks(mFlipRunnable); 163 postDelayed(mFlipRunnable, mFlipInterval); 164 } 165 super.showNext(); 166 } 167 168 /** 169 * {@inheritDoc} 170 */ 171 @Override 172 @RemotableViewMethod showPrevious()173 public void showPrevious() { 174 // if the flipper is currently flipping automatically, and showPrevious() is called 175 // we should we should make sure to reset the timer 176 if (mRunning) { 177 removeCallbacks(mFlipRunnable); 178 postDelayed(mFlipRunnable, mFlipInterval); 179 } 180 super.showPrevious(); 181 } 182 183 /** 184 * Internal method to start or stop dispatching flip {@link Message} based 185 * on {@link #mRunning} and {@link #mVisible} state. 186 */ updateRunning()187 private void updateRunning() { 188 // by default when we update running, we want the 189 // current view to animate in 190 updateRunning(true); 191 } 192 193 /** 194 * Internal method to start or stop dispatching flip {@link Message} based 195 * on {@link #mRunning} and {@link #mVisible} state. 196 * 197 * @param flipNow Determines whether or not to execute the animation now, in 198 * addition to queuing future flips. If omitted, defaults to 199 * true. 200 */ updateRunning(boolean flipNow)201 private void updateRunning(boolean flipNow) { 202 boolean running = !mAdvancedByHost && mVisible && mStarted && mAdapter != null; 203 if (running != mRunning) { 204 if (running) { 205 showOnly(mWhichChild, flipNow); 206 postDelayed(mFlipRunnable, mFlipInterval); 207 } else { 208 removeCallbacks(mFlipRunnable); 209 } 210 mRunning = running; 211 } 212 if (LOGD) { 213 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted 214 + ", mRunning=" + mRunning); 215 } 216 } 217 218 /** 219 * Returns true if the child views are flipping. 220 */ isFlipping()221 public boolean isFlipping() { 222 return mStarted; 223 } 224 225 /** 226 * Set if this view automatically calls {@link #startFlipping()} when it 227 * becomes attached to a window. 228 */ setAutoStart(boolean autoStart)229 public void setAutoStart(boolean autoStart) { 230 mAutoStart = autoStart; 231 } 232 233 /** 234 * Returns true if this view automatically calls {@link #startFlipping()} 235 * when it becomes attached to a window. 236 */ isAutoStart()237 public boolean isAutoStart() { 238 return mAutoStart; 239 } 240 241 private final Runnable mFlipRunnable = new Runnable() { 242 @Override 243 public void run() { 244 if (mRunning) { 245 showNext(); 246 } 247 } 248 }; 249 250 /** 251 * Called by an {@link android.appwidget.AppWidgetHost} to indicate that it will be 252 * automatically advancing the views of this {@link AdapterViewFlipper} by calling 253 * {@link AdapterViewFlipper#advance()} at some point in the future. This allows 254 * {@link AdapterViewFlipper} to prepare by no longer Advancing its children. 255 */ 256 @Override fyiWillBeAdvancedByHostKThx()257 public void fyiWillBeAdvancedByHostKThx() { 258 mAdvancedByHost = true; 259 updateRunning(false); 260 } 261 262 @Override getAccessibilityClassName()263 public CharSequence getAccessibilityClassName() { 264 return AdapterViewFlipper.class.getName(); 265 } 266 } 267