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.Message;
     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 
     52     public ViewFlipper(Context context) {
     53         super(context);
     54     }
     55 
     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
     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, getHandler());
    100 
    101         if (mAutoStart) {
    102             // Automatically start when requested
    103             startFlipping();
    104         }
    105     }
    106 
    107     @Override
    108     protected void onDetachedFromWindow() {
    109         super.onDetachedFromWindow();
    110         mVisible = false;
    111 
    112         getContext().unregisterReceiver(mReceiver);
    113         updateRunning();
    114     }
    115 
    116     @Override
    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
    130     public void setFlipInterval(int milliseconds) {
    131         mFlipInterval = milliseconds;
    132     }
    133 
    134     /**
    135      * Start a timer to cycle through child views
    136      */
    137     public void startFlipping() {
    138         mStarted = true;
    139         updateRunning();
    140     }
    141 
    142     /**
    143      * No more flips
    144      */
    145     public void stopFlipping() {
    146         mStarted = false;
    147         updateRunning();
    148     }
    149 
    150     @Override
    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      */
    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      */
    171     private void updateRunning(boolean flipNow) {
    172         boolean running = mVisible && mStarted && mUserPresent;
    173         if (running != mRunning) {
    174             if (running) {
    175                 showOnly(mWhichChild, flipNow);
    176                 postDelayed(mFlipRunnable, mFlipInterval);
    177             } else {
    178                 removeCallbacks(mFlipRunnable);
    179             }
    180             mRunning = running;
    181         }
    182         if (LOGD) {
    183             Log.d(TAG, "updateRunning() mVisible=" + mVisible + ", mStarted=" + mStarted
    184                     + ", mUserPresent=" + mUserPresent + ", mRunning=" + mRunning);
    185         }
    186     }
    187 
    188     /**
    189      * Returns true if the child views are flipping.
    190      */
    191     public boolean isFlipping() {
    192         return mStarted;
    193     }
    194 
    195     /**
    196      * Set if this view automatically calls {@link #startFlipping()} when it
    197      * becomes attached to a window.
    198      */
    199     public void setAutoStart(boolean autoStart) {
    200         mAutoStart = autoStart;
    201     }
    202 
    203     /**
    204      * Returns true if this view automatically calls {@link #startFlipping()}
    205      * when it becomes attached to a window.
    206      */
    207     public boolean isAutoStart() {
    208         return mAutoStart;
    209     }
    210 
    211     private final Runnable mFlipRunnable = new Runnable() {
    212         @Override
    213         public void run() {
    214             if (mRunning) {
    215                 showNext();
    216                 postDelayed(mFlipRunnable, mFlipInterval);
    217             }
    218         }
    219     };
    220 }
    221