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