• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ActionableToastBar(Context context)57     public ActionableToastBar(Context context) {
58         this(context, null);
59     }
60 
ActionableToastBar(Context context, AttributeSet attrs)61     public ActionableToastBar(Context context, AttributeSet attrs) {
62         this(context, attrs, 0);
63     }
64 
ActionableToastBar(Context context, AttributeSet attrs, int defStyle)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
onFinishInflate()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      */
show(final ActionClickedListener listener, int descriptionIconResourceId, CharSequence descriptionText, boolean showActionIcon, int actionTextResource, boolean replaceVisibleToast, final ToastBarOperation op)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 
getOperation()151     public ToastBarOperation getOperation() {
152         return mOperation;
153     }
154 
155     /**
156      * Hides the view and resets the state.
157      */
hide(boolean animate, boolean actionClicked)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 
getShowAnimation()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 
getHideAnimation()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 
isEventInToastBar(MotionEvent event)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 
isAnimating()237     public boolean isAnimating() {
238         return mShowAnimation != null && mShowAnimation.isStarted();
239     }
240 
241     @Override
onDetachedFromWindow()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 {
onActionClicked(Context context)252         public void onActionClicked(Context context);
253     }
254 }
255