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