ActionableToastBar.java revision baf6e244948b1db23e064e8eb2114185d5410695
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 */
16package com.android.mail.ui;
17
18import android.animation.Animator;
19import android.animation.AnimatorInflater;
20import android.content.Context;
21import android.os.Handler;
22import android.util.AttributeSet;
23import android.view.LayoutInflater;
24import android.view.MotionEvent;
25import android.view.View;
26import android.widget.ImageView;
27import android.widget.LinearLayout;
28import android.widget.TextView;
29
30import com.android.mail.R;
31
32/**
33 * A custom {@link View} that exposes an action to the user.
34 */
35public 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        if (!actionClicked && mOperation != null) {
160            mOperation.onToastBarTimeout(getContext());
161        }
162
163        mHidden = true;
164        mFadeOutHandler.removeCallbacks(mRunnable);
165        if (getVisibility() == View.VISIBLE) {
166            mActionDescriptionView.setText("");
167            mActionButton.setOnClickListener(null);
168            // Hide view once it's clicked.
169            if (animate) {
170                getHideAnimation().start();
171            } else {
172                setAlpha(0);
173                setVisibility(View.GONE);
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