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