Home | History | Annotate | Download | only in widget
      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