|
1 /* |
|
2 * Copyright 2012 Roman Nurik |
|
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 org.mozilla.gecko.widget; |
|
18 |
|
19 import java.util.LinkedList; |
|
20 |
|
21 import org.mozilla.gecko.R; |
|
22 import org.mozilla.gecko.animation.PropertyAnimator; |
|
23 |
|
24 import android.graphics.drawable.Drawable; |
|
25 import android.os.Bundle; |
|
26 import android.os.Handler; |
|
27 import android.view.View; |
|
28 import android.widget.Button; |
|
29 import android.widget.TextView; |
|
30 |
|
31 public class ButtonToast { |
|
32 private final static String LOGTAG = "GeckoButtonToast"; |
|
33 private final static int TOAST_DURATION = 5000; |
|
34 |
|
35 private final View mView; |
|
36 private final TextView mMessageView; |
|
37 private final Button mButton; |
|
38 private final Handler mHideHandler = new Handler(); |
|
39 |
|
40 private final ToastListener mListener; |
|
41 private final LinkedList<Toast> mQueue = new LinkedList<Toast>(); |
|
42 private Toast mCurrentToast; |
|
43 |
|
44 public enum ReasonHidden { |
|
45 CLICKED, |
|
46 TIMEOUT, |
|
47 STARTUP |
|
48 } |
|
49 |
|
50 // State objects |
|
51 private static class Toast { |
|
52 public final CharSequence buttonMessage; |
|
53 public Drawable buttonDrawable; |
|
54 public final CharSequence message; |
|
55 public ToastListener listener; |
|
56 |
|
57 public Toast(CharSequence aMessage, CharSequence aButtonMessage, |
|
58 Drawable aDrawable, ToastListener aListener) { |
|
59 message = aMessage; |
|
60 buttonMessage = aButtonMessage; |
|
61 buttonDrawable = aDrawable; |
|
62 listener = aListener; |
|
63 } |
|
64 } |
|
65 |
|
66 public interface ToastListener { |
|
67 void onButtonClicked(); |
|
68 void onToastHidden(ReasonHidden reason); |
|
69 } |
|
70 |
|
71 public ButtonToast(View view) { |
|
72 mView = view; |
|
73 mListener = null; |
|
74 mMessageView = (TextView) mView.findViewById(R.id.toast_message); |
|
75 mButton = (Button) mView.findViewById(R.id.toast_button); |
|
76 mButton.setOnClickListener(new View.OnClickListener() { |
|
77 @Override |
|
78 public void onClick(View view) { |
|
79 Toast t = mCurrentToast; |
|
80 if (t == null) |
|
81 return; |
|
82 |
|
83 hide(false, ReasonHidden.CLICKED); |
|
84 if (t.listener != null) { |
|
85 t.listener.onButtonClicked(); |
|
86 } |
|
87 } |
|
88 }); |
|
89 |
|
90 hide(true, ReasonHidden.STARTUP); |
|
91 } |
|
92 |
|
93 public void show(boolean immediate, CharSequence message, |
|
94 CharSequence buttonMessage, Drawable buttonDrawable, |
|
95 ToastListener listener) { |
|
96 show(new Toast(message, buttonMessage, buttonDrawable, listener), immediate); |
|
97 } |
|
98 |
|
99 private void show(Toast t, boolean immediate) { |
|
100 // If we're already showing a toast, add this one to the queue to show later |
|
101 if (mView.getVisibility() == View.VISIBLE) { |
|
102 mQueue.offer(t); |
|
103 return; |
|
104 } |
|
105 |
|
106 mCurrentToast = t; |
|
107 mButton.setEnabled(true); |
|
108 |
|
109 mMessageView.setText(t.message); |
|
110 mButton.setText(t.buttonMessage); |
|
111 mButton.setCompoundDrawablePadding(mView.getContext().getResources().getDimensionPixelSize(R.dimen.toast_button_padding)); |
|
112 mButton.setCompoundDrawablesWithIntrinsicBounds(t.buttonDrawable, null, null, null); |
|
113 |
|
114 mHideHandler.removeCallbacks(mHideRunnable); |
|
115 mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); |
|
116 |
|
117 mView.setVisibility(View.VISIBLE); |
|
118 int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); |
|
119 |
|
120 PropertyAnimator animator = new PropertyAnimator(duration); |
|
121 animator.attach(mView, PropertyAnimator.Property.ALPHA, 1.0f); |
|
122 animator.start(); |
|
123 } |
|
124 |
|
125 public void hide(boolean immediate, ReasonHidden reason) { |
|
126 if (mButton.isPressed() && reason != ReasonHidden.CLICKED) { |
|
127 mHideHandler.postDelayed(mHideRunnable, TOAST_DURATION); |
|
128 return; |
|
129 } |
|
130 |
|
131 if (mCurrentToast != null && mCurrentToast.listener != null) { |
|
132 mCurrentToast.listener.onToastHidden(reason); |
|
133 } |
|
134 mCurrentToast = null; |
|
135 mButton.setEnabled(false); |
|
136 mHideHandler.removeCallbacks(mHideRunnable); |
|
137 int duration = immediate ? 0 : mView.getResources().getInteger(android.R.integer.config_longAnimTime); |
|
138 |
|
139 mView.clearAnimation(); |
|
140 if (immediate) { |
|
141 mView.setVisibility(View.GONE); |
|
142 showNextInQueue(); |
|
143 } else { |
|
144 // Using Android's animation frameworks will not correctly turn off clicking. |
|
145 // See bug 885717. |
|
146 PropertyAnimator animator = new PropertyAnimator(duration); |
|
147 animator.attach(mView, PropertyAnimator.Property.ALPHA, 0.0f); |
|
148 animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener () { |
|
149 // If we are showing a toast and go in the background |
|
150 // onAnimationEnd will be called when the app is restored |
|
151 public void onPropertyAnimationEnd() { |
|
152 mView.setVisibility(View.GONE); |
|
153 showNextInQueue(); |
|
154 } |
|
155 public void onPropertyAnimationStart() { } |
|
156 }); |
|
157 animator.start(); |
|
158 } |
|
159 } |
|
160 |
|
161 public void onSaveInstanceState(Bundle outState) { |
|
162 // Add whatever toast we're currently showing to the front of the queue |
|
163 if (mCurrentToast != null) { |
|
164 mQueue.add(0, mCurrentToast); |
|
165 } |
|
166 } |
|
167 |
|
168 private void showNextInQueue() { |
|
169 Toast t = mQueue.poll(); |
|
170 if (t != null) { |
|
171 show(t, false); |
|
172 } |
|
173 } |
|
174 |
|
175 private Runnable mHideRunnable = new Runnable() { |
|
176 @Override |
|
177 public void run() { |
|
178 hide(false, ReasonHidden.TIMEOUT); |
|
179 } |
|
180 }; |
|
181 } |