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.Handler; 25import android.os.Message; 26import android.util.AttributeSet; 27import android.util.Log; 28import 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 39public 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 private boolean mUserPresent = true; 52 53 public ViewFlipper(Context context) { 54 super(context); 55 } 56 57 public ViewFlipper(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 60 TypedArray a = context.obtainStyledAttributes(attrs, 61 com.android.internal.R.styleable.ViewFlipper); 62 mFlipInterval = a.getInt( 63 com.android.internal.R.styleable.ViewFlipper_flipInterval, DEFAULT_INTERVAL); 64 mAutoStart = a.getBoolean( 65 com.android.internal.R.styleable.ViewFlipper_autoStart, false); 66 a.recycle(); 67 } 68 69 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 70 @Override 71 public void onReceive(Context context, Intent intent) { 72 final String action = intent.getAction(); 73 if (Intent.ACTION_SCREEN_OFF.equals(action)) { 74 mUserPresent = false; 75 updateRunning(); 76 } else if (Intent.ACTION_USER_PRESENT.equals(action)) { 77 mUserPresent = true; 78 updateRunning(); 79 } 80 } 81 }; 82 83 @Override 84 protected void onAttachedToWindow() { 85 super.onAttachedToWindow(); 86 87 // Listen for broadcasts related to user-presence 88 final IntentFilter filter = new IntentFilter(); 89 filter.addAction(Intent.ACTION_SCREEN_OFF); 90 filter.addAction(Intent.ACTION_USER_PRESENT); 91 getContext().registerReceiver(mReceiver, filter); 92 93 if (mAutoStart) { 94 // Automatically start when requested 95 startFlipping(); 96 } 97 } 98 99 @Override 100 protected void onDetachedFromWindow() { 101 super.onDetachedFromWindow(); 102 mVisible = false; 103 104 getContext().unregisterReceiver(mReceiver); 105 updateRunning(); 106 } 107 108 @Override 109 protected void onWindowVisibilityChanged(int visibility) { 110 super.onWindowVisibilityChanged(visibility); 111 mVisible = visibility == VISIBLE; 112 updateRunning(); 113 } 114 115 /** 116 * How long to wait before flipping to the next view 117 * 118 * @param milliseconds 119 * time in milliseconds 120 */ 121 @android.view.RemotableViewMethod 122 public void setFlipInterval(int milliseconds) { 123 mFlipInterval = milliseconds; 124 } 125 126 /** 127 * Start a timer to cycle through child views 128 */ 129 public void startFlipping() { 130 mStarted = true; 131 updateRunning(); 132 } 133 134 /** 135 * No more flips 136 */ 137 public void stopFlipping() { 138 mStarted = false; 139 updateRunning(); 140 } 141 142 /** 143 * Internal method to start or stop dispatching flip {@link Message} based 144 * on {@link #mRunning} and {@link #mVisible} state. 145 */ 146 private void updateRunning() { 147 boolean running = mVisible && mStarted && mUserPresent; 148 if (running != mRunning) { 149 if (running) { 150 showOnly(mWhichChild); 151 Message msg = mHandler.obtainMessage(FLIP_MSG); 152 mHandler.sendMessageDelayed(msg, mFlipInterval); 153 } else { 154 mHandler.removeMessages(FLIP_MSG); 155 } 156 mRunning = running; 157 } 158 if (LOGD) { 159 Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted 160 + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning); 161 } 162 } 163 164 /** 165 * Returns true if the child views are flipping. 166 */ 167 public boolean isFlipping() { 168 return mStarted; 169 } 170 171 /** 172 * Set if this view automatically calls {@link #startFlipping()} when it 173 * becomes attached to a window. 174 */ 175 public void setAutoStart(boolean autoStart) { 176 mAutoStart = autoStart; 177 } 178 179 /** 180 * Returns true if this view automatically calls {@link #startFlipping()} 181 * when it becomes attached to a window. 182 */ 183 public boolean isAutoStart() { 184 return mAutoStart; 185 } 186 187 private final int FLIP_MSG = 1; 188 189 private final Handler mHandler = new Handler() { 190 @Override 191 public void handleMessage(Message msg) { 192 if (msg.what == FLIP_MSG) { 193 if (mRunning) { 194 showNext(); 195 msg = obtainMessage(FLIP_MSG); 196 sendMessageDelayed(msg, mFlipInterval); 197 } 198 } 199 } 200 }; 201} 202