blob: c8a7c079994c01875118aaafe920714bfe469383 [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
Bernardo Rufino3231f612019-12-20 18:04:48 +000019import static com.android.internal.util.Preconditions.checkNotNull;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +000020import static com.android.internal.util.Preconditions.checkState;
Bernardo Rufino3231f612019-12-20 18:04:48 +000021
Tor Norbyed9273d62013-05-30 15:59:53 -070022import android.annotation.IntDef;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -040023import android.annotation.NonNull;
24import android.annotation.Nullable;
Tor Norbye7b9c9122013-05-30 16:48:33 -070025import android.annotation.StringRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.app.INotificationManager;
27import android.app.ITransientNotification;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +000028import android.app.ITransientNotificationCallback;
29import android.compat.Compatibility;
30import android.compat.annotation.ChangeId;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000031import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.Context;
Fabrice Di Meglio025f9602012-07-30 18:53:09 -070033import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.res.Resources;
35import android.graphics.PixelFormat;
Bernardo Rufino52af6db2019-12-20 18:26:07 +000036import android.os.Binder;
Mathew Inwood8c854f82018-09-14 12:35:36 +010037import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.os.Handler;
Svetoslav Ganovaa076532016-08-01 19:16:43 -070039import android.os.IBinder;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -040040import android.os.Looper;
Svetoslav Ganovaa076532016-08-01 19:16:43 -070041import android.os.Message;
svetoslavganov75986cf2009-05-14 22:28:01 -070042import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.ServiceManager;
44import android.util.Log;
45import android.view.Gravity;
46import android.view.LayoutInflater;
47import android.view.View;
48import android.view.WindowManager;
Svetoslav Ganovcd2613a2010-10-05 15:55:39 -070049import android.view.accessibility.AccessibilityEvent;
50import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
Bernardo Rufino3231f612019-12-20 18:04:48 +000052import com.android.internal.annotations.GuardedBy;
53
Tor Norbyed9273d62013-05-30 15:59:53 -070054import java.lang.annotation.Retention;
55import java.lang.annotation.RetentionPolicy;
Bernardo Rufino3231f612019-12-20 18:04:48 +000056import java.util.ArrayList;
57import java.util.List;
Tor Norbyed9273d62013-05-30 15:59:53 -070058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059/**
60 * A toast is a view containing a quick little message for the user. The toast class
61 * helps you create and show those.
62 * {@more}
63 *
64 * <p>
65 * When the view is shown to the user, appears as a floating view over the
66 * application. It will never receive focus. The user will probably be in the
67 * middle of typing something else. The idea is to be as unobtrusive as
68 * possible, while still showing the user the information you want them to see.
69 * Two examples are the volume control, and the brief message saying that your
70 * settings have been saved.
71 * <p>
72 * The easiest way to use this class is to call one of the static methods that constructs
73 * everything you need and returns a new Toast object.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +000074 * <p>
75 * Note that
76 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbars</a> are
77 * preferred for brief messages while the app is in the foreground.
Joe Fernandez558459f2011-10-13 16:47:36 -070078 *
79 * <div class="special reference">
80 * <h3>Developer Guides</h3>
81 * <p>For information about creating Toast notifications, read the
82 * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer
83 * guide.</p>
84 * </div>
Geoffrey Pitschd34c1872017-05-04 16:02:15 -040085 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086public class Toast {
87 static final String TAG = "Toast";
88 static final boolean localLOGV = false;
89
Tor Norbyed9273d62013-05-30 15:59:53 -070090 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070091 @IntDef(prefix = { "LENGTH_" }, value = {
92 LENGTH_SHORT,
93 LENGTH_LONG
94 })
Tor Norbyed9273d62013-05-30 15:59:53 -070095 @Retention(RetentionPolicy.SOURCE)
96 public @interface Duration {}
97
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 /**
99 * Show the view or text notification for a short period of time. This time
100 * could be user-definable. This is the default.
101 * @see #setDuration
102 */
103 public static final int LENGTH_SHORT = 0;
104
105 /**
106 * Show the view or text notification for a long period of time. This time
107 * could be user-definable.
108 * @see #setDuration
109 */
110 public static final int LENGTH_LONG = 1;
111
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000112 /**
113 * Text toasts will be rendered by SystemUI instead of in-app, so apps can't circumvent
114 * background custom toast restrictions.
115 *
116 * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
117 */
118 @ChangeId
119 // @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
120 private static final long CHANGE_TEXT_TOASTS_IN_THE_SYSTEM = 147798919L;
121
122
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000123 private final Binder mToken;
124 private final Context mContext;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000125 private final Handler mHandler;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800126 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 final TN mTN;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100128 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 int mDuration;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000130
131 /**
132 * This is also passed to {@link TN} object, where it's also accessed with itself as its own
133 * lock.
134 */
135 @GuardedBy("mCallbacks")
136 private final List<Callback> mCallbacks;
137
138 /**
139 * View to be displayed, in case this is a custom toast (e.g. not created with {@link
140 * #makeText(Context, int, int)} or its variants).
141 */
142 @Nullable
143 private View mNextView;
144
145 /**
146 * Text to be shown, in case this is NOT a custom toast (e.g. created with {@link
147 * #makeText(Context, int, int)} or its variants).
148 */
149 @Nullable
150 private CharSequence mText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151
152 /**
153 * Construct an empty Toast object. You must call {@link #setView} before you
154 * can call {@link #show}.
155 *
156 * @param context The context to use. Usually your {@link android.app.Application}
157 * or {@link android.app.Activity} object.
158 */
159 public Toast(Context context) {
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400160 this(context, null);
161 }
162
163 /**
164 * Constructs an empty Toast object. If looper is null, Looper.myLooper() is used.
165 * @hide
166 */
167 public Toast(@NonNull Context context, @Nullable Looper looper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 mContext = context;
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000169 mToken = new Binder();
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000170 looper = getLooper(looper);
171 mHandler = new Handler(looper);
172 mCallbacks = new ArrayList<>();
173 mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper);
Romain Guycf3dd6b2011-01-19 16:54:13 -0800174 mTN.mY = context.getResources().getDimensionPixelSize(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 com.android.internal.R.dimen.toast_y_offset);
Jose Lima08bef372013-08-08 11:51:00 -0700176 mTN.mGravity = context.getResources().getInteger(
177 com.android.internal.R.integer.config_toastDefaultGravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
Robert Carr6cfc4e32016-10-05 15:33:16 -0700179
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000180 private Looper getLooper(@Nullable Looper looper) {
181 if (looper != null) {
182 return looper;
183 }
184 return checkNotNull(Looper.myLooper(),
185 "Can't toast on a thread that has not called Looper.prepare()");
186 }
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 /**
189 * Show the view for the specified duration.
190 */
191 public void show() {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000192 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
193 checkState(mNextView != null || mText != null, "You must either set a text or a view");
194 } else {
195 if (mNextView == null) {
196 throw new RuntimeException("setView must have been called");
197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 }
199
200 INotificationManager service = getService();
Tony Hill929442d2014-09-16 11:27:35 +0100201 String pkg = mContext.getOpPackageName();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800202 TN tn = mTN;
203 tn.mNextView = mNextView;
Yohei Yukawa5281b6b2018-10-15 07:38:25 +0800204 final int displayId = mContext.getDisplayId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 try {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000207 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
208 if (mNextView != null) {
209 // It's a custom toast
210 service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
211 } else {
212 // It's a text toast
213 ITransientNotificationCallback callback =
214 new CallbackBinder(mCallbacks, mHandler);
215 service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
216 }
Bernardo Rufino60c39b32019-11-08 16:11:15 +0000217 } else {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000218 service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
Bernardo Rufino60c39b32019-11-08 16:11:15 +0000219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 } catch (RemoteException e) {
221 // Empty
222 }
223 }
224
225 /**
226 * Close the view if it's showing, or don't show it if it isn't showing yet.
227 * You do not normally have to call this. Normally view will disappear on its own
228 * after the appropriate duration.
229 */
230 public void cancel() {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000231 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)
232 && mNextView == null) {
233 try {
234 getService().cancelToast(mContext.getOpPackageName(), mToken);
235 } catch (RemoteException e) {
236 // Empty
237 }
238 } else {
239 mTN.cancel();
240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
Robert Carr6cfc4e32016-10-05 15:33:16 -0700242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /**
244 * Set the view to show.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000245 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 * @see #getView
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000247 * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
248 * {@link #makeText(Context, CharSequence, int)} method, or use a
249 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
250 * when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
251 * targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
252 * will not have custom toast views displayed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 */
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000254 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 public void setView(View view) {
256 mNextView = view;
257 }
258
259 /**
260 * Return the view.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000261 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * @see #setView
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000263 * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
264 * {@link #makeText(Context, CharSequence, int)} method, or use a
265 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
266 * when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
267 * targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
268 * will not have custom toast views displayed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 */
270 public View getView() {
271 return mNextView;
272 }
273
274 /**
275 * Set how long to show the view for.
276 * @see #LENGTH_SHORT
277 * @see #LENGTH_LONG
278 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700279 public void setDuration(@Duration int duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 mDuration = duration;
Robert Carr70f0d222016-04-10 16:33:08 -0700281 mTN.mDuration = duration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
283
284 /**
285 * Return the duration.
286 * @see #setDuration
287 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700288 @Duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 public int getDuration() {
290 return mDuration;
291 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 /**
294 * Set the margins of the view.
295 *
296 * @param horizontalMargin The horizontal margin, in percentage of the
297 * container width, between the container's edges and the
298 * notification
299 * @param verticalMargin The vertical margin, in percentage of the
300 * container height, between the container's edges and the
301 * notification
302 */
303 public void setMargin(float horizontalMargin, float verticalMargin) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800304 mTN.mHorizontalMargin = horizontalMargin;
305 mTN.mVerticalMargin = verticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
307
308 /**
309 * Return the horizontal margin.
310 */
311 public float getHorizontalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800312 return mTN.mHorizontalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
314
315 /**
316 * Return the vertical margin.
317 */
318 public float getVerticalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800319 return mTN.mVerticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 }
321
322 /**
323 * Set the location at which the notification should appear on the screen.
324 * @see android.view.Gravity
325 * @see #getGravity
326 */
327 public void setGravity(int gravity, int xOffset, int yOffset) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800328 mTN.mGravity = gravity;
329 mTN.mX = xOffset;
330 mTN.mY = yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332
333 /**
334 * Get the location at which the notification should appear on the screen.
335 * @see android.view.Gravity
336 * @see #getGravity
337 */
338 public int getGravity() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800339 return mTN.mGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341
342 /**
343 * Return the X offset in pixels to apply to the gravity's location.
344 */
345 public int getXOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800346 return mTN.mX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 /**
350 * Return the Y offset in pixels to apply to the gravity's location.
351 */
352 public int getYOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800353 return mTN.mY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 }
Jason Monka5852682014-10-23 11:48:36 -0400355
356 /**
Bernardo Rufino3231f612019-12-20 18:04:48 +0000357 * Adds a callback to be notified when the toast is shown or hidden.
358 *
359 * Note that if the toast is blocked for some reason you won't get a call back.
360 *
361 * @see #removeCallback(Callback)
362 */
363 public void addCallback(@NonNull Callback callback) {
364 checkNotNull(callback);
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000365 synchronized (mCallbacks) {
366 mCallbacks.add(callback);
Bernardo Rufino3231f612019-12-20 18:04:48 +0000367 }
368 }
369
370 /**
371 * Removes a callback previously added with {@link #addCallback(Callback)}.
372 */
373 public void removeCallback(@NonNull Callback callback) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000374 synchronized (mCallbacks) {
375 mCallbacks.remove(callback);
Bernardo Rufino3231f612019-12-20 18:04:48 +0000376 }
377 }
378
379 /**
Jason Monka5852682014-10-23 11:48:36 -0400380 * Gets the LayoutParams for the Toast window.
381 * @hide
382 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100383 @UnsupportedAppUsage
Jason Monka5852682014-10-23 11:48:36 -0400384 public WindowManager.LayoutParams getWindowParams() {
385 return mTN.mParams;
386 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
389 * Make a standard toast that just contains a text view.
390 *
391 * @param context The context to use. Usually your {@link android.app.Application}
392 * or {@link android.app.Activity} object.
393 * @param text The text to show. Can be formatted text.
394 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
395 * {@link #LENGTH_LONG}
396 *
397 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700398 public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400399 return makeText(context, null, text, duration);
400 }
401
402 /**
403 * Make a standard toast to display using the specified looper.
404 * If looper is null, Looper.myLooper() is used.
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000405 *
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400406 * @hide
407 */
408 public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
409 @NonNull CharSequence text, @Duration int duration) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000410 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
411 Toast result = new Toast(context, looper);
412 result.mText = text;
413 result.mDuration = duration;
414 return result;
415 } else {
416 Toast result = new Toast(context, looper);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000418 LayoutInflater inflate = (LayoutInflater)
419 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
420 View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
421 TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
422 tv.setText(text);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400423
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000424 result.mNextView = v;
425 result.mDuration = duration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000427 return result;
428 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 }
430
431 /**
432 * Make a standard toast that just contains a text view with the text from a resource.
433 *
434 * @param context The context to use. Usually your {@link android.app.Application}
435 * or {@link android.app.Activity} object.
436 * @param resId The resource id of the string resource to use. Can be formatted text.
437 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
438 * {@link #LENGTH_LONG}
439 *
440 * @throws Resources.NotFoundException if the resource can't be found.
441 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700442 public static Toast makeText(Context context, @StringRes int resId, @Duration int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 throws Resources.NotFoundException {
444 return makeText(context, context.getResources().getText(resId), duration);
445 }
446
447 /**
448 * Update the text in a Toast that was previously created using one of the makeText() methods.
449 * @param resId The new text for the Toast.
450 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700451 public void setText(@StringRes int resId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 setText(mContext.getText(resId));
453 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 /**
456 * Update the text in a Toast that was previously created using one of the makeText() methods.
457 * @param s The new text for the Toast.
458 */
459 public void setText(CharSequence s) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000460 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
461 if (mNextView != null) {
462 throw new IllegalStateException(
463 "Text provided for custom toast, remove previous setView() calls if you "
464 + "want a text toast instead.");
465 }
466 mText = s;
467 } else {
468 if (mNextView == null) {
469 throw new RuntimeException("This Toast was not created with Toast.makeText()");
470 }
471 TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
472 if (tv == null) {
473 throw new RuntimeException("This Toast was not created with Toast.makeText()");
474 }
475 tv.setText(s);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700478
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 // =======================================================================================
480 // All the gunk below is the interaction with the Notification Service, which handles
481 // the proper ordering of these system-wide.
482 // =======================================================================================
483
Adam Powellc7ac9f02019-02-04 13:29:11 -0800484 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 private static INotificationManager sService;
486
Adam Powellc7ac9f02019-02-04 13:29:11 -0800487 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guy0420cc72009-07-08 11:59:00 -0700488 static private INotificationManager getService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 if (sService != null) {
490 return sService;
491 }
492 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
493 return sService;
494 }
495
Romain Guyc4d09f22011-01-19 10:55:19 -0800496 private static class TN extends ITransientNotification.Stub {
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800497 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guy0420cc72009-07-08 11:59:00 -0700498 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
Robert Carr6cfc4e32016-10-05 15:33:16 -0700499
500 private static final int SHOW = 0;
501 private static final int HIDE = 1;
502 private static final int CANCEL = 2;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400503 final Handler mHandler;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800504
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800505 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Jose Lima08bef372013-08-08 11:51:00 -0700506 int mGravity;
Mathew Inwood74e7aed2018-08-16 17:29:31 +0100507 int mX;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800508 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Mathew Inwood74e7aed2018-08-16 17:29:31 +0100509 int mY;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800510 float mHorizontalMargin;
511 float mVerticalMargin;
512
Jose Lima08bef372013-08-08 11:51:00 -0700513
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800514 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guycf3dd6b2011-01-19 16:54:13 -0800515 View mView;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800516 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guycf3dd6b2011-01-19 16:54:13 -0800517 View mNextView;
Robert Carr70f0d222016-04-10 16:33:08 -0700518 int mDuration;
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700519
Jeff Brown98365d72012-08-19 20:30:52 -0700520 WindowManager mWM;
Romain Guy0420cc72009-07-08 11:59:00 -0700521
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000522 final String mPackageName;
523 final Binder mToken;
Robert Carr6cfc4e32016-10-05 15:33:16 -0700524
Bernardo Rufino3231f612019-12-20 18:04:48 +0000525 @GuardedBy("mCallbacks")
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000526 private final List<Callback> mCallbacks;
Bernardo Rufino3231f612019-12-20 18:04:48 +0000527
Robert Carr017dbf52016-07-21 18:16:00 -0700528 static final long SHORT_DURATION_TIMEOUT = 4000;
529 static final long LONG_DURATION_TIMEOUT = 7000;
Robert Carr70f0d222016-04-10 16:33:08 -0700530
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000531 /**
532 * Creates a {@link ITransientNotification} object.
533 *
534 * The parameter {@code callbacks} is not copied and is accessed with itself as its own
535 * lock.
536 */
537 TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 // XXX This should be changed to use a Dialog, with a Theme.Toast
539 // defined that sets up the layout params appropriately.
Romain Guy0420cc72009-07-08 11:59:00 -0700540 final WindowManager.LayoutParams params = mParams;
541 params.height = WindowManager.LayoutParams.WRAP_CONTENT;
542 params.width = WindowManager.LayoutParams.WRAP_CONTENT;
Romain Guy0420cc72009-07-08 11:59:00 -0700543 params.format = PixelFormat.TRANSLUCENT;
544 params.windowAnimations = com.android.internal.R.style.Animation_Toast;
545 params.type = WindowManager.LayoutParams.TYPE_TOAST;
Tiger Huang52724442020-01-20 21:38:42 +0800546 params.setFitInsetsIgnoringVisibility(true);
Romain Guy0420cc72009-07-08 11:59:00 -0700547 params.setTitle("Toast");
John Spurlockf1a36642013-10-12 17:50:42 -0400548 params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
John Spurlockdcf4f212013-05-21 17:19:53 -0400549 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
John Spurlockf1a36642013-10-12 17:50:42 -0400550 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
Robert Carr6cfc4e32016-10-05 15:33:16 -0700551
552 mPackageName = packageName;
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000553 mToken = token;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000554 mCallbacks = callbacks;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400555
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400556 mHandler = new Handler(looper, null) {
557 @Override
558 public void handleMessage(Message msg) {
559 switch (msg.what) {
560 case SHOW: {
561 IBinder token = (IBinder) msg.obj;
562 handleShow(token);
563 break;
564 }
565 case HIDE: {
566 handleHide();
567 // Don't do this in handleHide() because it is also invoked by
568 // handleShow()
569 mNextView = null;
570 break;
571 }
572 case CANCEL: {
573 handleHide();
574 // Don't do this in handleHide() because it is also invoked by
575 // handleShow()
576 mNextView = null;
577 try {
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000578 getService().cancelToast(mPackageName, mToken);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400579 } catch (RemoteException e) {
580 }
581 break;
582 }
583 }
584 }
585 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 }
587
Bernardo Rufino3231f612019-12-20 18:04:48 +0000588 private List<Callback> getCallbacks() {
589 synchronized (mCallbacks) {
590 return new ArrayList<>(mCallbacks);
591 }
592 }
593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 /**
595 * schedule handleShow into the right thread
596 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700597 @Override
Mathew Inwood8c854f82018-09-14 12:35:36 +0100598 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700599 public void show(IBinder windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 if (localLOGV) Log.v(TAG, "SHOW: " + this);
Robert Carr6cfc4e32016-10-05 15:33:16 -0700601 mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 }
603
604 /**
605 * schedule handleHide into the right thread
606 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700607 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700608 public void hide() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 if (localLOGV) Log.v(TAG, "HIDE: " + this);
Robert Carr6cfc4e32016-10-05 15:33:16 -0700610 mHandler.obtainMessage(HIDE).sendToTarget();
611 }
612
613 public void cancel() {
614 if (localLOGV) Log.v(TAG, "CANCEL: " + this);
615 mHandler.obtainMessage(CANCEL).sendToTarget();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
617
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700618 public void handleShow(IBinder windowToken) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800619 if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
620 + " mNextView=" + mNextView);
Svet Ganov0df37022017-05-15 23:45:03 -0700621 // If a cancel/hide is pending - no need to show - at this point
622 // the window token is already invalid and no need to do any work.
623 if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
624 return;
625 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800626 if (mView != mNextView) {
627 // remove the old view if necessary
628 handleHide();
629 mView = mNextView;
Craig Mautner54ae2f32012-12-05 09:49:57 -0800630 Context context = mView.getContext().getApplicationContext();
Tony Hill929442d2014-09-16 11:27:35 +0100631 String packageName = mView.getContext().getOpPackageName();
Craig Mautner54ae2f32012-12-05 09:49:57 -0800632 if (context == null) {
633 context = mView.getContext();
634 }
635 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Fabrice Di Megliob4946342012-06-07 16:29:18 -0700636 // We can resolve the Gravity here by using the Locale for getting
637 // the layout direction
Fabrice Di Meglio025f9602012-07-30 18:53:09 -0700638 final Configuration config = mView.getContext().getResources().getConfiguration();
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700639 final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
Romain Guycf3dd6b2011-01-19 16:54:13 -0800640 mParams.gravity = gravity;
641 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
642 mParams.horizontalWeight = 1.0f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800644 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
645 mParams.verticalWeight = 1.0f;
646 }
647 mParams.x = mX;
648 mParams.y = mY;
649 mParams.verticalMargin = mVerticalMargin;
650 mParams.horizontalMargin = mHorizontalMargin;
Tony Hill929442d2014-09-16 11:27:35 +0100651 mParams.packageName = packageName;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700652 mParams.hideTimeoutMilliseconds = mDuration ==
Robert Carr70f0d222016-04-10 16:33:08 -0700653 Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700654 mParams.token = windowToken;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800655 if (mView.getParent() != null) {
656 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
657 mWM.removeView(mView);
658 }
659 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
Svet Ganov0df37022017-05-15 23:45:03 -0700660 // Since the notification manager service cancels the token right
661 // after it notifies us to cancel the toast there is an inherent
662 // race and we may attempt to add a window after the token has been
663 // invalidated. Let us hedge against that.
664 try {
665 mWM.addView(mView, mParams);
666 trySendAccessibilityEvent();
Bernardo Rufino3231f612019-12-20 18:04:48 +0000667 for (Callback callback : getCallbacks()) {
668 callback.onToastShown();
669 }
Svet Ganov0df37022017-05-15 23:45:03 -0700670 } catch (WindowManager.BadTokenException e) {
671 /* ignore */
672 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 }
674 }
675
Romain Guycf3dd6b2011-01-19 16:54:13 -0800676 private void trySendAccessibilityEvent() {
677 AccessibilityManager accessibilityManager =
678 AccessibilityManager.getInstance(mView.getContext());
Eugene Suslad4128ec2017-12-04 19:48:41 +0000679 if (!accessibilityManager.isEnabled()) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800680 return;
681 }
682 // treat toasts as notifications since they are used to
683 // announce a transient piece of information to the user
684 AccessibilityEvent event = AccessibilityEvent.obtain(
685 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
686 event.setClassName(getClass().getName());
687 event.setPackageName(mView.getContext().getPackageName());
688 mView.dispatchPopulateAccessibilityEvent(event);
689 accessibilityManager.sendAccessibilityEvent(event);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400690 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800691
Mathew Inwood978c6e22018-08-21 15:58:55 +0100692 @UnsupportedAppUsage
Romain Guy0420cc72009-07-08 11:59:00 -0700693 public void handleHide() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800694 if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
695 if (mView != null) {
696 // note: checking parent() just to make sure the view has
697 // been added... i have seen cases where we get here when
698 // the view isn't yet added, so let's try not to crash.
699 if (mView.getParent() != null) {
700 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
Svet Ganov1777c912016-09-08 07:11:52 -0700701 mWM.removeViewImmediate(mView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800703
Robert Carr997427342018-02-28 18:06:10 -0800704
705 // Now that we've removed the view it's safe for the server to release
706 // the resources.
707 try {
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000708 getService().finishToken(mPackageName, mToken);
Robert Carr997427342018-02-28 18:06:10 -0800709 } catch (RemoteException e) {
710 }
711
Bernardo Rufino3231f612019-12-20 18:04:48 +0000712 for (Callback callback : getCallbacks()) {
713 callback.onToastHidden();
714 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800715 mView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 }
Bernardo Rufino3231f612019-12-20 18:04:48 +0000719
720 /**
721 * Callback object to be called when the toast is shown or hidden.
722 *
723 * Callback methods will be called on the looper thread provided on construction.
724 *
725 * @see #addCallback(Callback)
726 */
727 public abstract static class Callback {
728 /**
729 * Called when the toast is displayed on the screen.
730 */
731 public void onToastShown() {}
732
733 /**
734 * Called when the toast is hidden.
735 */
736 public void onToastHidden() {}
737 }
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000738
739 private static class CallbackBinder extends ITransientNotificationCallback.Stub {
740 private final Handler mHandler;
741
742 @GuardedBy("mCallbacks")
743 private final List<Callback> mCallbacks;
744
745 /**
746 * Creates a {@link ITransientNotificationCallback} object.
747 *
748 * The parameter {@code callbacks} is not copied and is accessed with itself as its own
749 * lock.
750 */
751 private CallbackBinder(List<Callback> callbacks, Handler handler) {
752 mCallbacks = callbacks;
753 mHandler = handler;
754 }
755
756 @Override
757 public void onToastShown() {
758 mHandler.post(() -> {
759 for (Callback callback : getCallbacks()) {
760 callback.onToastShown();
761 }
762 });
763 }
764
765 @Override
766 public void onToastHidden() {
767 mHandler.post(() -> {
768 for (Callback callback : getCallbacks()) {
769 callback.onToastHidden();
770 }
771 });
772 }
773
774 private List<Callback> getCallbacks() {
775 synchronized (mCallbacks) {
776 return new ArrayList<>(mCallbacks);
777 }
778 }
779 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780}