Home | History | Annotate | Download | only in ui
      1 /**
      2  * Copyright (c) 2011, Google Inc.
      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 package com.android.mail.ui;
     17 
     18 import android.animation.Animator;
     19 import android.animation.AnimatorInflater;
     20 import android.content.Context;
     21 import android.os.Handler;
     22 import android.util.AttributeSet;
     23 import android.view.LayoutInflater;
     24 import android.view.MotionEvent;
     25 import android.view.View;
     26 import android.widget.ImageView;
     27 import android.widget.LinearLayout;
     28 import android.widget.TextView;
     29 
     30 import com.android.mail.R;
     31 
     32 /**
     33  * A custom {@link View} that exposes an action to the user.
     34  */
     35 public class ActionableToastBar extends LinearLayout {
     36     private boolean mHidden = false;
     37     private Animator mShowAnimation;
     38     private Animator mHideAnimation;
     39     private final Runnable mRunnable;
     40     private final Handler mFadeOutHandler;
     41 
     42     /** How long toast will last in ms */
     43     private static final long TOAST_LIFETIME = 15*1000L;
     44 
     45     /** Icon for the description. */
     46     private ImageView mActionDescriptionIcon;
     47     /** The clickable view */
     48     private View mActionButton;
     49     /** Icon for the action button. */
     50     private View mActionIcon;
     51     /** The view that contains the description. */
     52     private TextView mActionDescriptionView;
     53     /** The view that contains the text for the action button. */
     54     private TextView mActionText;
     55     private ToastBarOperation mOperation;
     56 
     57     public ActionableToastBar(Context context) {
     58         this(context, null);
     59     }
     60 
     61     public ActionableToastBar(Context context, AttributeSet attrs) {
     62         this(context, attrs, 0);
     63     }
     64 
     65     public ActionableToastBar(Context context, AttributeSet attrs, int defStyle) {
     66         super(context, attrs, defStyle);
     67         mFadeOutHandler = new Handler();
     68         mRunnable = new Runnable() {
     69             @Override
     70             public void run() {
     71                 if(!mHidden) {
     72                     hide(true, false /* actionClicked */);
     73                 }
     74             }
     75         };
     76         LayoutInflater.from(context).inflate(R.layout.actionable_toast_row, this, true);
     77     }
     78 
     79     @Override
     80     protected void onFinishInflate() {
     81         super.onFinishInflate();
     82 
     83         mActionDescriptionIcon = (ImageView) findViewById(R.id.description_icon);
     84         mActionDescriptionView = (TextView) findViewById(R.id.description_text);
     85         mActionButton = findViewById(R.id.action_button);
     86         mActionIcon = findViewById(R.id.action_icon);
     87         mActionText = (TextView) findViewById(R.id.action_text);
     88     }
     89 
     90     /**
     91      * Displays the toast bar and makes it visible. Allows the setting of
     92      * parameters to customize the display.
     93      * @param listener Performs some action when the action button is clicked.
     94      *                 If the {@link ToastBarOperation} overrides
     95      *                 {@link ToastBarOperation#shouldTakeOnActionClickedPrecedence()}
     96      *                 to return <code>true</code>, the
     97      *                 {@link ToastBarOperation#onActionClicked(android.content.Context)}
     98      *                 will override this listener and be called instead.
     99      * @param descriptionIconResourceId resource ID for the description icon or
    100      *                                  0 if no icon should be shown
    101      * @param descriptionText a description text to show in the toast bar
    102      * @param showActionIcon if true, the action button icon should be shown
    103      * @param actionTextResource resource ID for the text to show in the action button
    104      * @param replaceVisibleToast if true, this toast should replace any currently visible toast.
    105      *                            Otherwise, skip showing this toast.
    106      * @param op the operation that corresponds to the specific toast being shown
    107      */
    108     public void show(final ActionClickedListener listener, int descriptionIconResourceId,
    109             CharSequence descriptionText, boolean showActionIcon, int actionTextResource,
    110             boolean replaceVisibleToast, final ToastBarOperation op) {
    111 
    112         if (!mHidden && !replaceVisibleToast) {
    113             return;
    114         }
    115         // Remove any running delayed animations first
    116         mFadeOutHandler.removeCallbacks(mRunnable);
    117 
    118         mOperation = op;
    119 
    120         mActionButton.setOnClickListener(new OnClickListener() {
    121             @Override
    122             public void onClick(View widget) {
    123                 if (op.shouldTakeOnActionClickedPrecedence()) {
    124                     op.onActionClicked(getContext());
    125                 } else {
    126                     listener.onActionClicked(getContext());
    127                 }
    128                 hide(true /* animate */, true /* actionClicked */);
    129             }
    130         });
    131 
    132         // Set description icon.
    133         if (descriptionIconResourceId == 0) {
    134             mActionDescriptionIcon.setVisibility(GONE);
    135         } else {
    136             mActionDescriptionIcon.setVisibility(VISIBLE);
    137             mActionDescriptionIcon.setImageResource(descriptionIconResourceId);
    138         }
    139 
    140         mActionDescriptionView.setText(descriptionText);
    141         mActionIcon.setVisibility(showActionIcon ? VISIBLE : GONE);
    142         mActionText.setText(actionTextResource);
    143 
    144         mHidden = false;
    145         getShowAnimation().start();
    146 
    147         // Set up runnable to execute hide toast once delay is completed
    148         mFadeOutHandler.postDelayed(mRunnable, TOAST_LIFETIME);
    149     }
    150 
    151     public ToastBarOperation getOperation() {
    152         return mOperation;
    153     }
    154 
    155     /**
    156      * Hides the view and resets the state.
    157      */
    158     public void hide(boolean animate, boolean actionClicked) {
    159         mHidden = true;
    160         mFadeOutHandler.removeCallbacks(mRunnable);
    161         if (getVisibility() == View.VISIBLE) {
    162             mActionDescriptionView.setText("");
    163             mActionButton.setOnClickListener(null);
    164             // Hide view once it's clicked.
    165             if (animate) {
    166                 getHideAnimation().start();
    167             } else {
    168                 setAlpha(0);
    169                 setVisibility(View.GONE);
    170             }
    171 
    172             if (!actionClicked && mOperation != null) {
    173                 mOperation.onToastBarTimeout(getContext());
    174             }
    175         }
    176     }
    177 
    178     private Animator getShowAnimation() {
    179         if (mShowAnimation == null) {
    180             mShowAnimation = AnimatorInflater.loadAnimator(getContext(),
    181                     R.anim.fade_in);
    182             mShowAnimation.addListener(new Animator.AnimatorListener() {
    183                 @Override
    184                 public void onAnimationStart(Animator animation) {
    185                     setVisibility(View.VISIBLE);
    186                 }
    187                 @Override
    188                 public void onAnimationEnd(Animator animation) {
    189                 }
    190                 @Override
    191                 public void onAnimationCancel(Animator animation) {
    192                 }
    193                 @Override
    194                 public void onAnimationRepeat(Animator animation) {
    195                 }
    196             });
    197             mShowAnimation.setTarget(this);
    198         }
    199         return mShowAnimation;
    200     }
    201 
    202     private Animator getHideAnimation() {
    203         if (mHideAnimation == null) {
    204             mHideAnimation = AnimatorInflater.loadAnimator(getContext(),
    205                     R.anim.fade_out);
    206             mHideAnimation.addListener(new Animator.AnimatorListener() {
    207                 @Override
    208                 public void onAnimationStart(Animator animation) {
    209                 }
    210                 @Override
    211                 public void onAnimationRepeat(Animator animation) {
    212                 }
    213                 @Override
    214                 public void onAnimationEnd(Animator animation) {
    215                     setVisibility(View.GONE);
    216                 }
    217                 @Override
    218                 public void onAnimationCancel(Animator animation) {
    219                 }
    220             });
    221             mHideAnimation.setTarget(this);
    222         }
    223         return mHideAnimation;
    224     }
    225 
    226     public boolean isEventInToastBar(MotionEvent event) {
    227         if (!isShown()) {
    228             return false;
    229         }
    230         int[] xy = new int[2];
    231         float x = event.getX();
    232         float y = event.getY();
    233         getLocationOnScreen(xy);
    234         return (x > xy[0] && x < (xy[0] + getWidth()) && y > xy[1] && y < xy[1] + getHeight());
    235     }
    236 
    237     public boolean isAnimating() {
    238         return mShowAnimation != null && mShowAnimation.isStarted();
    239     }
    240 
    241     @Override
    242     public void onDetachedFromWindow() {
    243         mFadeOutHandler.removeCallbacks(mRunnable);
    244         super.onDetachedFromWindow();
    245     }
    246 
    247     /**
    248      * Classes that wish to perform some action when the action button is clicked
    249      * should implement this interface.
    250      */
    251     public interface ActionClickedListener {
    252         public void onActionClicked(Context context);
    253     }
    254 }
    255