1 /*
2  * Copyright (C) 2012 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 com.android.deskclock.widget;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorInflater;
21 import android.animation.AnimatorListenerAdapter;
22 import android.content.Context;
23 import android.util.AttributeSet;
24 import android.view.LayoutInflater;
25 import android.view.MotionEvent;
26 import android.view.View;
27 import android.widget.FrameLayout;
28 import android.widget.ImageView;
29 import android.widget.LinearLayout;
30 import android.widget.TextView;
31 
32 import com.android.deskclock.R;
33 
34 /**
35  * A custom {@link View} that exposes an action to the user.
36  * <p>
37  * This is a copy of packages/apps/UnifiedEmail/src/com/android/mail/ui/ActionableToastBar.java
38  * with minor modifications.
39  */
40 public class ActionableToastBar extends LinearLayout {
41     private boolean mHidden = false;
42     private Animator mShowAnimation;
43     private Animator mHideAnimation;
44     private final int mBottomMarginSizeInConversation;
45 
46     /** Icon for the description. */
47     private ImageView mActionDescriptionIcon;
48     /** The clickable view */
49     private View mActionButton;
50     /** Icon for the action button. */
51     private View mActionIcon;
52     /** The view that contains the description. */
53     private TextView mActionDescriptionView;
54     /** The view that contains the text for the action button. */
55     private TextView mActionText;
56     //private ToastBarOperation mOperation;
57 
ActionableToastBar(Context context)58     public ActionableToastBar(Context context) {
59         this(context, null);
60     }
61 
ActionableToastBar(Context context, AttributeSet attrs)62     public ActionableToastBar(Context context, AttributeSet attrs) {
63         this(context, attrs, 0);
64     }
65 
ActionableToastBar(Context context, AttributeSet attrs, int defStyle)66     public ActionableToastBar(Context context, AttributeSet attrs, int defStyle) {
67         super(context, attrs, defStyle);
68         mBottomMarginSizeInConversation = context.getResources().getDimensionPixelSize(
69                 R.dimen.toast_bar_bottom_margin_in_conversation);
70         LayoutInflater.from(context).inflate(R.layout.actionable_toast_row, this, true);
71     }
72 
73     @Override
onFinishInflate()74     protected void onFinishInflate() {
75         super.onFinishInflate();
76 
77         mActionDescriptionIcon = (ImageView) findViewById(R.id.description_icon);
78         mActionDescriptionView = (TextView) findViewById(R.id.description_text);
79         mActionButton = findViewById(R.id.action_button);
80         mActionIcon = findViewById(R.id.action_icon);
81         mActionText = (TextView) findViewById(R.id.action_text);
82     }
83 
84     /**
85      * Tells the view that it will be appearing in the conversation pane
86      * and should adjust its layout parameters accordingly.
87      * @param isInConversationMode true if the view will be shown in the conversation view
88      */
setConversationMode(boolean isInConversationMode)89     public void setConversationMode(boolean isInConversationMode) {
90         final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) getLayoutParams();
91         params.bottomMargin = isInConversationMode ? mBottomMarginSizeInConversation : 0;
92         setLayoutParams(params);
93     }
94 
95     /**
96      * Displays the toast bar and makes it visible. Allows the setting of
97      * parameters to customize the display.
98      * @param listener performs some action when the action button is clicked
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      */
show(final ActionClickedListener listener, int descriptionIconResourceId, CharSequence descriptionText, boolean showActionIcon, int actionTextResource, boolean replaceVisibleToast)107     public void show(final ActionClickedListener listener, int descriptionIconResourceId,
108             CharSequence descriptionText, boolean showActionIcon, int actionTextResource,
109             boolean replaceVisibleToast) {
110 
111         if (!mHidden && !replaceVisibleToast) {
112             return;
113         }
114 
115         mActionButton.setOnClickListener(new OnClickListener() {
116             @Override
117             public void onClick(View widget) {
118                 if (listener != null) {
119                     listener.onActionClicked();
120                 }
121                 hide(true);
122             }
123         });
124 
125         // Set description icon.
126         if (descriptionIconResourceId == 0) {
127             mActionDescriptionIcon.setVisibility(GONE);
128         } else {
129             mActionDescriptionIcon.setVisibility(VISIBLE);
130             mActionDescriptionIcon.setImageResource(descriptionIconResourceId);
131         }
132 
133         mActionDescriptionView.setText(descriptionText);
134         mActionIcon.setVisibility(showActionIcon ? VISIBLE : GONE);
135         mActionText.setText(actionTextResource);
136 
137         mHidden = false;
138         getShowAnimation().start();
139     }
140 
141     /**
142      * Hides the view and resets the state.
143      */
hide(boolean animate)144     public void hide(boolean animate) {
145         // Prevent multiple call to hide.
146         // Also prevent hiding if show animation is going on.
147         if (!mHidden && !getShowAnimation().isRunning()) {
148             mHidden = true;
149             if (getVisibility() == View.VISIBLE) {
150                 mActionDescriptionView.setText("");
151                 mActionButton.setOnClickListener(null);
152                 // Hide view once it's clicked.
153                 if (animate) {
154                     getHideAnimation().start();
155                 } else {
156                     setAlpha(0);
157                     setVisibility(View.GONE);
158                 }
159             }
160         }
161     }
162 
getShowAnimation()163     private Animator getShowAnimation() {
164         if (mShowAnimation == null) {
165             mShowAnimation = AnimatorInflater.loadAnimator(getContext(), R.animator.fade_in);
166             mShowAnimation.addListener(new AnimatorListenerAdapter() {
167                 @Override
168                 public void onAnimationStart(Animator animation) {
169                     setVisibility(View.VISIBLE);
170                 }
171 
172                 @Override
173                 public void onAnimationEnd(Animator animation) {
174                     // There is a tiny change that and hide animation could have finished right
175                     // before the show animation finished.  In that case, the hide will mark the
176                     // view as GONE.  We need to make sure the last one wins.
177                     setVisibility(View.VISIBLE);
178                 }
179             });
180             mShowAnimation.setTarget(this);
181         }
182         return mShowAnimation;
183     }
184 
getHideAnimation()185     private Animator getHideAnimation() {
186         if (mHideAnimation == null) {
187             mHideAnimation = AnimatorInflater.loadAnimator(getContext(), R.animator.fade_out);
188             mHideAnimation.addListener(new AnimatorListenerAdapter() {
189                 @Override
190                 public void onAnimationEnd(Animator animation) {
191                     setVisibility(View.GONE);
192                 }
193             });
194             mHideAnimation.setTarget(this);
195         }
196         return mHideAnimation;
197     }
198 
isEventInToastBar(MotionEvent event)199     public boolean isEventInToastBar(MotionEvent event) {
200         if (!isShown()) {
201             return false;
202         }
203         int[] xy = new int[2];
204         float x = event.getX();
205         float y = event.getY();
206         getLocationOnScreen(xy);
207         return (x > xy[0] && x < (xy[0] + getWidth()) && y > xy[1] && y < xy[1] + getHeight());
208     }
209 
210     /**
211      * Classes that wish to perform some action when the action button is clicked
212      * should implement this interface.
213      */
214     public interface ActionClickedListener {
onActionClicked()215         public void onActionClicked();
216     }
217 }
218