blob: bf5e49b9fea56fb7a7721bce09afd779530a6173 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package android.widget;
18
Tor Norbyed9273d62013-05-30 15:59:53 -070019import android.annotation.IntDef;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.INotificationManager;
21import android.app.ITransientNotification;
22import android.content.Context;
Fabrice Di Meglio025f9602012-07-30 18:53:09 -070023import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.res.Resources;
25import android.graphics.PixelFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.os.Handler;
svetoslavganov75986cf2009-05-14 22:28:01 -070027import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.ServiceManager;
29import android.util.Log;
30import android.view.Gravity;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.WindowManager;
Svetoslav Ganovcd2613a2010-10-05 15:55:39 -070034import android.view.accessibility.AccessibilityEvent;
35import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
Tor Norbyed9273d62013-05-30 15:59:53 -070037import java.lang.annotation.Retention;
38import java.lang.annotation.RetentionPolicy;
39
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040/**
41 * A toast is a view containing a quick little message for the user. The toast class
42 * helps you create and show those.
43 * {@more}
44 *
45 * <p>
46 * When the view is shown to the user, appears as a floating view over the
47 * application. It will never receive focus. The user will probably be in the
48 * middle of typing something else. The idea is to be as unobtrusive as
49 * possible, while still showing the user the information you want them to see.
50 * Two examples are the volume control, and the brief message saying that your
51 * settings have been saved.
52 * <p>
53 * The easiest way to use this class is to call one of the static methods that constructs
54 * everything you need and returns a new Toast object.
Joe Fernandez558459f2011-10-13 16:47:36 -070055 *
56 * <div class="special reference">
57 * <h3>Developer Guides</h3>
58 * <p>For information about creating Toast notifications, read the
59 * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer
60 * guide.</p>
61 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 */
63public class Toast {
64 static final String TAG = "Toast";
65 static final boolean localLOGV = false;
66
Tor Norbyed9273d62013-05-30 15:59:53 -070067 /** @hide */
68 @IntDef({LENGTH_SHORT, LENGTH_LONG})
69 @Retention(RetentionPolicy.SOURCE)
70 public @interface Duration {}
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 /**
73 * Show the view or text notification for a short period of time. This time
74 * could be user-definable. This is the default.
75 * @see #setDuration
76 */
77 public static final int LENGTH_SHORT = 0;
78
79 /**
80 * Show the view or text notification for a long period of time. This time
81 * could be user-definable.
82 * @see #setDuration
83 */
84 public static final int LENGTH_LONG = 1;
85
86 final Context mContext;
87 final TN mTN;
88 int mDuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 View mNextView;
90
91 /**
92 * Construct an empty Toast object. You must call {@link #setView} before you
93 * can call {@link #show}.
94 *
95 * @param context The context to use. Usually your {@link android.app.Application}
96 * or {@link android.app.Activity} object.
97 */
98 public Toast(Context context) {
99 mContext = context;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800100 mTN = new TN();
101 mTN.mY = context.getResources().getDimensionPixelSize(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 com.android.internal.R.dimen.toast_y_offset);
Jose Lima08bef372013-08-08 11:51:00 -0700103 mTN.mGravity = context.getResources().getInteger(
104 com.android.internal.R.integer.config_toastDefaultGravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 }
106
107 /**
108 * Show the view for the specified duration.
109 */
110 public void show() {
111 if (mNextView == null) {
112 throw new RuntimeException("setView must have been called");
113 }
114
115 INotificationManager service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 String pkg = mContext.getPackageName();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800117 TN tn = mTN;
118 tn.mNextView = mNextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 try {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800121 service.enqueueToast(pkg, tn, mDuration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 } catch (RemoteException e) {
123 // Empty
124 }
125 }
126
127 /**
128 * Close the view if it's showing, or don't show it if it isn't showing yet.
129 * You do not normally have to call this. Normally view will disappear on its own
130 * after the appropriate duration.
131 */
132 public void cancel() {
133 mTN.hide();
Jeff Brown51f24302012-05-20 12:07:26 -0700134
135 try {
136 getService().cancelToast(mContext.getPackageName(), mTN);
137 } catch (RemoteException e) {
138 // Empty
139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 }
141
142 /**
143 * Set the view to show.
144 * @see #getView
145 */
146 public void setView(View view) {
147 mNextView = view;
148 }
149
150 /**
151 * Return the view.
152 * @see #setView
153 */
154 public View getView() {
155 return mNextView;
156 }
157
158 /**
159 * Set how long to show the view for.
160 * @see #LENGTH_SHORT
161 * @see #LENGTH_LONG
162 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700163 public void setDuration(@Duration int duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 mDuration = duration;
165 }
166
167 /**
168 * Return the duration.
169 * @see #setDuration
170 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700171 @Duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 public int getDuration() {
173 return mDuration;
174 }
175
176 /**
177 * Set the margins of the view.
178 *
179 * @param horizontalMargin The horizontal margin, in percentage of the
180 * container width, between the container's edges and the
181 * notification
182 * @param verticalMargin The vertical margin, in percentage of the
183 * container height, between the container's edges and the
184 * notification
185 */
186 public void setMargin(float horizontalMargin, float verticalMargin) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800187 mTN.mHorizontalMargin = horizontalMargin;
188 mTN.mVerticalMargin = verticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 }
190
191 /**
192 * Return the horizontal margin.
193 */
194 public float getHorizontalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800195 return mTN.mHorizontalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 }
197
198 /**
199 * Return the vertical margin.
200 */
201 public float getVerticalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800202 return mTN.mVerticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204
205 /**
206 * Set the location at which the notification should appear on the screen.
207 * @see android.view.Gravity
208 * @see #getGravity
209 */
210 public void setGravity(int gravity, int xOffset, int yOffset) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800211 mTN.mGravity = gravity;
212 mTN.mX = xOffset;
213 mTN.mY = yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
215
216 /**
217 * Get the location at which the notification should appear on the screen.
218 * @see android.view.Gravity
219 * @see #getGravity
220 */
221 public int getGravity() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800222 return mTN.mGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 }
224
225 /**
226 * Return the X offset in pixels to apply to the gravity's location.
227 */
228 public int getXOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800229 return mTN.mX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 }
231
232 /**
233 * Return the Y offset in pixels to apply to the gravity's location.
234 */
235 public int getYOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800236 return mTN.mY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 }
238
239 /**
240 * Make a standard toast that just contains a text view.
241 *
242 * @param context The context to use. Usually your {@link android.app.Application}
243 * or {@link android.app.Activity} object.
244 * @param text The text to show. Can be formatted text.
245 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
246 * {@link #LENGTH_LONG}
247 *
248 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700249 public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 Toast result = new Toast(context);
251
Romain Guy0420cc72009-07-08 11:59:00 -0700252 LayoutInflater inflate = (LayoutInflater)
253 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
255 TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
256 tv.setText(text);
257
258 result.mNextView = v;
259 result.mDuration = duration;
260
261 return result;
262 }
263
264 /**
265 * Make a standard toast that just contains a text view with the text from a resource.
266 *
267 * @param context The context to use. Usually your {@link android.app.Application}
268 * or {@link android.app.Activity} object.
269 * @param resId The resource id of the string resource to use. Can be formatted text.
270 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
271 * {@link #LENGTH_LONG}
272 *
273 * @throws Resources.NotFoundException if the resource can't be found.
274 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700275 public static Toast makeText(Context context, int resId, @Duration int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 throws Resources.NotFoundException {
277 return makeText(context, context.getResources().getText(resId), duration);
278 }
279
280 /**
281 * Update the text in a Toast that was previously created using one of the makeText() methods.
282 * @param resId The new text for the Toast.
283 */
284 public void setText(int resId) {
285 setText(mContext.getText(resId));
286 }
287
288 /**
289 * Update the text in a Toast that was previously created using one of the makeText() methods.
290 * @param s The new text for the Toast.
291 */
292 public void setText(CharSequence s) {
293 if (mNextView == null) {
294 throw new RuntimeException("This Toast was not created with Toast.makeText()");
295 }
296 TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
297 if (tv == null) {
298 throw new RuntimeException("This Toast was not created with Toast.makeText()");
299 }
300 tv.setText(s);
301 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 // =======================================================================================
304 // All the gunk below is the interaction with the Notification Service, which handles
305 // the proper ordering of these system-wide.
306 // =======================================================================================
307
308 private static INotificationManager sService;
309
Romain Guy0420cc72009-07-08 11:59:00 -0700310 static private INotificationManager getService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 if (sService != null) {
312 return sService;
313 }
314 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
315 return sService;
316 }
317
Romain Guyc4d09f22011-01-19 10:55:19 -0800318 private static class TN extends ITransientNotification.Stub {
Romain Guy0420cc72009-07-08 11:59:00 -0700319 final Runnable mShow = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700320 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700321 public void run() {
322 handleShow();
323 }
324 };
325
326 final Runnable mHide = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700327 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700328 public void run() {
329 handleHide();
Romain Guy2152ca52011-01-21 12:31:53 -0800330 // Don't do this in handleHide() because it is also invoked by handleShow()
331 mNextView = null;
Romain Guy0420cc72009-07-08 11:59:00 -0700332 }
333 };
334
335 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800336 final Handler mHandler = new Handler();
337
Jose Lima08bef372013-08-08 11:51:00 -0700338 int mGravity;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800339 int mX, mY;
340 float mHorizontalMargin;
341 float mVerticalMargin;
342
Jose Lima08bef372013-08-08 11:51:00 -0700343
Romain Guycf3dd6b2011-01-19 16:54:13 -0800344 View mView;
345 View mNextView;
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700346
Jeff Brown98365d72012-08-19 20:30:52 -0700347 WindowManager mWM;
Romain Guy0420cc72009-07-08 11:59:00 -0700348
Romain Guycf3dd6b2011-01-19 16:54:13 -0800349 TN() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 // XXX This should be changed to use a Dialog, with a Theme.Toast
351 // defined that sets up the layout params appropriately.
Romain Guy0420cc72009-07-08 11:59:00 -0700352 final WindowManager.LayoutParams params = mParams;
353 params.height = WindowManager.LayoutParams.WRAP_CONTENT;
354 params.width = WindowManager.LayoutParams.WRAP_CONTENT;
Romain Guy0420cc72009-07-08 11:59:00 -0700355 params.format = PixelFormat.TRANSLUCENT;
356 params.windowAnimations = com.android.internal.R.style.Animation_Toast;
357 params.type = WindowManager.LayoutParams.TYPE_TOAST;
358 params.setTitle("Toast");
John Spurlockf1a36642013-10-12 17:50:42 -0400359 params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
John Spurlockdcf4f212013-05-21 17:19:53 -0400360 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
John Spurlockf1a36642013-10-12 17:50:42 -0400361 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363
364 /**
365 * schedule handleShow into the right thread
366 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700367 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700368 public void show() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 if (localLOGV) Log.v(TAG, "SHOW: " + this);
370 mHandler.post(mShow);
371 }
372
373 /**
374 * schedule handleHide into the right thread
375 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700376 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700377 public void hide() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 if (localLOGV) Log.v(TAG, "HIDE: " + this);
379 mHandler.post(mHide);
380 }
381
Romain Guy0420cc72009-07-08 11:59:00 -0700382 public void handleShow() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800383 if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
384 + " mNextView=" + mNextView);
385 if (mView != mNextView) {
386 // remove the old view if necessary
387 handleHide();
388 mView = mNextView;
Craig Mautner54ae2f32012-12-05 09:49:57 -0800389 Context context = mView.getContext().getApplicationContext();
390 if (context == null) {
391 context = mView.getContext();
392 }
393 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Fabrice Di Megliob4946342012-06-07 16:29:18 -0700394 // We can resolve the Gravity here by using the Locale for getting
395 // the layout direction
Fabrice Di Meglio025f9602012-07-30 18:53:09 -0700396 final Configuration config = mView.getContext().getResources().getConfiguration();
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700397 final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
Romain Guycf3dd6b2011-01-19 16:54:13 -0800398 mParams.gravity = gravity;
399 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
400 mParams.horizontalWeight = 1.0f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800402 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
403 mParams.verticalWeight = 1.0f;
404 }
405 mParams.x = mX;
406 mParams.y = mY;
407 mParams.verticalMargin = mVerticalMargin;
408 mParams.horizontalMargin = mHorizontalMargin;
409 if (mView.getParent() != null) {
410 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
411 mWM.removeView(mView);
412 }
413 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
414 mWM.addView(mView, mParams);
415 trySendAccessibilityEvent();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417 }
418
Romain Guycf3dd6b2011-01-19 16:54:13 -0800419 private void trySendAccessibilityEvent() {
420 AccessibilityManager accessibilityManager =
421 AccessibilityManager.getInstance(mView.getContext());
422 if (!accessibilityManager.isEnabled()) {
423 return;
424 }
425 // treat toasts as notifications since they are used to
426 // announce a transient piece of information to the user
427 AccessibilityEvent event = AccessibilityEvent.obtain(
428 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
429 event.setClassName(getClass().getName());
430 event.setPackageName(mView.getContext().getPackageName());
431 mView.dispatchPopulateAccessibilityEvent(event);
432 accessibilityManager.sendAccessibilityEvent(event);
433 }
434
Romain Guy0420cc72009-07-08 11:59:00 -0700435 public void handleHide() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800436 if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
437 if (mView != null) {
438 // note: checking parent() just to make sure the view has
439 // been added... i have seen cases where we get here when
440 // the view isn't yet added, so let's try not to crash.
441 if (mView.getParent() != null) {
442 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
443 mWM.removeView(mView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800445
446 mView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
448 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450}