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.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.content.res.TypedArray; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.accessibility.AccessibilityEvent; 29 import android.view.accessibility.AccessibilityNodeInfo; 30 import android.widget.RemoteViews.RemoteView; 31 32 /** 33 * Simple {@link ViewAnimator} that will animate between two or more views 34 * that have been added to it. Only one child is shown at a time. If 35 * requested, can automatically flip between each child at a regular interval. 36 * 37 * @attr ref android.R.styleable#ViewFlipper_flipInterval 38 * @attr ref android.R.styleable#ViewFlipper_autoStart 39 */ 40 @RemoteView 41 public class ViewFlipper extends ViewAnimator { 42 private static final String TAG = "ViewFlipper"; 43 private static final boolean LOGD = false; 44 45 private static final int DEFAULT_INTERVAL = 3000; 46 47 private int mFlipInterval = DEFAULT_INTERVAL; 48 private boolean mAutoStart = false; 49 50 private boolean mRunning = false; 51 private boolean mStarted = false; 52 private boolean mVisible = false; 53 private boolean mUserPresent = true; 54 55 public ViewFlipper(Context context) { 56 super(context); 57 } 58 59 public ViewFlipper(Context context, AttributeSet attrs) { 60 super(context, attrs); 61 62 TypedArray a = context.obtainStyledAttributes(attrs, 63 com.android.internal.R.styleable.ViewFlipper); 64 mFlipInterval = a.getInt( 65 com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL); 66 mAutoStart = a.getBoolean( 67 com.android.internal.R.styleable.ViewFlipper_autoStart, false); 68 a.recycle(); 69 } 70 71 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 72 @Override 73 public void onReceive(Context context, Intent intent) { 74 final String action = intent.getAction(); 75 if (Intent.ACTION_SCREEN_OFF.equals(action)) { 76 mUserPresent = false; 77 updateRunning(); 78 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 79 mUserPresent = true; 80 updateRunning(false); 81 } 82 } 83 }; 84 85 @Override 86 protected void onAttachedToWindow() { 87 super.onAttachedToWindow(); 88 89 // Listen for broadcasts related to user-presence 90 final IntentFilter filter = new IntentFilter(); 91 filter.addAction(Intent.ACTION_SCREEN_OFF); 92 filter.addAction(Intent.ACTION_USER_PRESENT); 93 getContext().registerReceiver(mReceiver, filter); 94 95 if (mAutoStart) { 96 // Automatically start when requested 97 startFlipping(); 98 } 99 } 100 101 @Override 102 protected void onDetachedFromWindow() { 103 super.onDetachedFromWindow(); 104 mVisible = false; 105 106 getContext().unregisterReceiver(mReceiver); 107 updateRunning(); 108 } 109 110 @Override 111 protected void onWindowVisibilityChanged(int visibility) { 112 super.onWindowVisibilityChanged(visibility); 113 mVisible = visibility == VISIBLE; 114 updateRunning(false); 115 } 116 117 /** 118 * How long to wait before flipping to the next view 119 * 120 * @param milliseconds 121 * time in milliseconds 122 */ 123 @android.view.RemotableViewMethod 124 public void setFlipInterval(int milliseconds) { 125 mFlipInterval = milliseconds; 126 } 127 128 /** 129 * Start a timer to cycle through child views 130 */ 131 public void startFlipping() { 132 mStarted = true; 133 updateRunning(); 134 } 135 136 /** 137 * No more flips 138 */ 139 public void stopFlipping() { 140 mStarted = false; 141 updateRunning(); 142 } 143 144 @Override 145 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 146 super.onInitializeAccessibilityEvent(event); 147 event.setClassName(ViewFlipper.class.getName()); 148 } 149 150 @Override 151 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 152 super.onInitializeAccessibilityNodeInfo(info); 153 info.setClassName(ViewFlipper.class.getName()); 154 } 155 156 /** 157 * Internal method to start or stop dispatching flip {@link Message} based 158 * on {@link #mRunning} and {@link #mVisible} state. 159 */ 160 private void updateRunning() { 161 updateRunning(true); 162 } 163 164 /** 165 * Internal method to start or stop dispatching flip {@link Message} based 166 * on {@link #mRunning} and {@link #mVisible} state. 167 * 168 * @param flipNow Determines whether or not to execute the animation now, in 169 * addition to queuing future flips. If omitted, defaults to 170 * true. 171 */ 172 private void updateRunning(boolean flipNow) { 173 boolean running = mVisible && mStarted && mUserPresent; 174 if (running != mRunning) { 175 if (running) { 176 showOnly(mWhichChild, flipNow); 177 Message msg = mHandler.obtainMessage(FLIP_MSG); 178 mHandler.sendMessageDelayed(msg, mFlipInterval); 179 } else { 180 mHandler.removeMessages(FLIP_MSG); 181 } 182 mRunning = running; 183 } 184 if (LOGD) { 185 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted 186 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning); 187 } 188 } 189 190 /** 191 * Returns true if the child views are flipping. 192 */ 193 public boolean isFlipping() { 194 return mStarted; 195 } 196 197 /** 198 * Set if this view automatically calls {@link #startFlipping()} when it 199 * becomes attached to a window. 200 */ 201 public void setAutoStart(boolean autoStart) { 202 mAutoStart = autoStart; 203 } 204 205 /** 206 * Returns true if this view automatically calls {@link #startFlipping()} 207 * when it becomes attached to a window. 208 */ 209 public boolean isAutoStart() { 210 return mAutoStart; 211 } 212 213 private final int FLIP_MSG = 1; 214 215 private final Handler mHandler = new Handler() { 216 @Override 217 public void handleMessage(Message msg) { 218 if (msg.what == FLIP_MSG) { 219 if (mRunning) { 220 showNext(); 221 msg = obtainMessage(FLIP_MSG); 222 sendMessageDelayed(msg, mFlipInterval); 223 } 224 } 225 } 226 }; 227 } 228