blob: 78d4e61ea953c15cfaa3e6b3ee7fc6e5bfebbce2 [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;
Bernardo Rufino944121a2020-01-29 11:55:20 +000031import android.compat.annotation.EnabledAfter;
Artur Satayeved5a6ae2019-12-10 17:47:54 +000032import android.compat.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.content.Context;
Fabrice Di Meglio025f9602012-07-30 18:53:09 -070034import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.res.Resources;
36import android.graphics.PixelFormat;
Bernardo Rufino52af6db2019-12-20 18:26:07 +000037import android.os.Binder;
Mathew Inwood8c854f82018-09-14 12:35:36 +010038import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.Handler;
Svetoslav Ganovaa076532016-08-01 19:16:43 -070040import android.os.IBinder;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -040041import android.os.Looper;
Svetoslav Ganovaa076532016-08-01 19:16:43 -070042import android.os.Message;
svetoslavganov75986cf2009-05-14 22:28:01 -070043import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.ServiceManager;
45import android.util.Log;
46import android.view.Gravity;
47import android.view.LayoutInflater;
48import android.view.View;
49import android.view.WindowManager;
Svetoslav Ganovcd2613a2010-10-05 15:55:39 -070050import android.view.accessibility.AccessibilityEvent;
51import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Bernardo Rufino3231f612019-12-20 18:04:48 +000053import com.android.internal.annotations.GuardedBy;
54
Tor Norbyed9273d62013-05-30 15:59:53 -070055import java.lang.annotation.Retention;
56import java.lang.annotation.RetentionPolicy;
Bernardo Rufino3231f612019-12-20 18:04:48 +000057import java.util.ArrayList;
58import java.util.List;
Tor Norbyed9273d62013-05-30 15:59:53 -070059
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060/**
61 * A toast is a view containing a quick little message for the user. The toast class
62 * helps you create and show those.
63 * {@more}
64 *
65 * <p>
66 * When the view is shown to the user, appears as a floating view over the
67 * application. It will never receive focus. The user will probably be in the
68 * middle of typing something else. The idea is to be as unobtrusive as
69 * possible, while still showing the user the information you want them to see.
70 * Two examples are the volume control, and the brief message saying that your
71 * settings have been saved.
72 * <p>
73 * The easiest way to use this class is to call one of the static methods that constructs
74 * everything you need and returns a new Toast object.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +000075 * <p>
76 * Note that
77 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbars</a> are
78 * preferred for brief messages while the app is in the foreground.
Joe Fernandez558459f2011-10-13 16:47:36 -070079 *
80 * <div class="special reference">
81 * <h3>Developer Guides</h3>
82 * <p>For information about creating Toast notifications, read the
83 * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer
84 * guide.</p>
85 * </div>
Geoffrey Pitschd34c1872017-05-04 16:02:15 -040086 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087public class Toast {
88 static final String TAG = "Toast";
89 static final boolean localLOGV = false;
90
Tor Norbyed9273d62013-05-30 15:59:53 -070091 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070092 @IntDef(prefix = { "LENGTH_" }, value = {
93 LENGTH_SHORT,
94 LENGTH_LONG
95 })
Tor Norbyed9273d62013-05-30 15:59:53 -070096 @Retention(RetentionPolicy.SOURCE)
97 public @interface Duration {}
98
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 /**
100 * Show the view or text notification for a short period of time. This time
101 * could be user-definable. This is the default.
102 * @see #setDuration
103 */
104 public static final int LENGTH_SHORT = 0;
105
106 /**
107 * Show the view or text notification for a long period of time. This time
108 * could be user-definable.
109 * @see #setDuration
110 */
111 public static final int LENGTH_LONG = 1;
112
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000113 /**
114 * Text toasts will be rendered by SystemUI instead of in-app, so apps can't circumvent
115 * background custom toast restrictions.
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000116 */
117 @ChangeId
Bernardo Rufino944121a2020-01-29 11:55:20 +0000118 @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000119 private static final long CHANGE_TEXT_TOASTS_IN_THE_SYSTEM = 147798919L;
120
121
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000122 private final Binder mToken;
123 private final Context mContext;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000124 private final Handler mHandler;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800125 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 final TN mTN;
Mathew Inwood978c6e22018-08-21 15:58:55 +0100127 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 int mDuration;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000129
130 /**
131 * This is also passed to {@link TN} object, where it's also accessed with itself as its own
132 * lock.
133 */
134 @GuardedBy("mCallbacks")
135 private final List<Callback> mCallbacks;
136
137 /**
138 * View to be displayed, in case this is a custom toast (e.g. not created with {@link
139 * #makeText(Context, int, int)} or its variants).
140 */
141 @Nullable
142 private View mNextView;
143
144 /**
145 * Text to be shown, in case this is NOT a custom toast (e.g. created with {@link
146 * #makeText(Context, int, int)} or its variants).
147 */
148 @Nullable
149 private CharSequence mText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150
151 /**
152 * Construct an empty Toast object. You must call {@link #setView} before you
153 * can call {@link #show}.
154 *
155 * @param context The context to use. Usually your {@link android.app.Application}
156 * or {@link android.app.Activity} object.
157 */
158 public Toast(Context context) {
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400159 this(context, null);
160 }
161
162 /**
163 * Constructs an empty Toast object. If looper is null, Looper.myLooper() is used.
164 * @hide
165 */
166 public Toast(@NonNull Context context, @Nullable Looper looper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 mContext = context;
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000168 mToken = new Binder();
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000169 looper = getLooper(looper);
170 mHandler = new Handler(looper);
171 mCallbacks = new ArrayList<>();
172 mTN = new TN(context.getPackageName(), mToken, mCallbacks, looper);
Romain Guycf3dd6b2011-01-19 16:54:13 -0800173 mTN.mY = context.getResources().getDimensionPixelSize(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 com.android.internal.R.dimen.toast_y_offset);
Jose Lima08bef372013-08-08 11:51:00 -0700175 mTN.mGravity = context.getResources().getInteger(
176 com.android.internal.R.integer.config_toastDefaultGravity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 }
Robert Carr6cfc4e32016-10-05 15:33:16 -0700178
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000179 private Looper getLooper(@Nullable Looper looper) {
180 if (looper != null) {
181 return looper;
182 }
183 return checkNotNull(Looper.myLooper(),
184 "Can't toast on a thread that has not called Looper.prepare()");
185 }
186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 /**
188 * Show the view for the specified duration.
189 */
190 public void show() {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000191 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
192 checkState(mNextView != null || mText != null, "You must either set a text or a view");
193 } else {
194 if (mNextView == null) {
195 throw new RuntimeException("setView must have been called");
196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 }
198
199 INotificationManager service = getService();
Tony Hill929442d2014-09-16 11:27:35 +0100200 String pkg = mContext.getOpPackageName();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800201 TN tn = mTN;
202 tn.mNextView = mNextView;
Yohei Yukawa5281b6b2018-10-15 07:38:25 +0800203 final int displayId = mContext.getDisplayId();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 try {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000206 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
207 if (mNextView != null) {
208 // It's a custom toast
209 service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
210 } else {
211 // It's a text toast
212 ITransientNotificationCallback callback =
213 new CallbackBinder(mCallbacks, mHandler);
214 service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
215 }
Bernardo Rufino60c39b32019-11-08 16:11:15 +0000216 } else {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000217 service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
Bernardo Rufino60c39b32019-11-08 16:11:15 +0000218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 } catch (RemoteException e) {
220 // Empty
221 }
222 }
223
224 /**
225 * Close the view if it's showing, or don't show it if it isn't showing yet.
226 * You do not normally have to call this. Normally view will disappear on its own
227 * after the appropriate duration.
228 */
229 public void cancel() {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000230 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)
231 && mNextView == null) {
232 try {
233 getService().cancelToast(mContext.getOpPackageName(), mToken);
234 } catch (RemoteException e) {
235 // Empty
236 }
237 } else {
238 mTN.cancel();
239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 }
Robert Carr6cfc4e32016-10-05 15:33:16 -0700241
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 /**
243 * Set the view to show.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000244 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 * @see #getView
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000246 * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
247 * {@link #makeText(Context, CharSequence, int)} method, or use a
248 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
249 * when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
250 * targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
251 * will not have custom toast views displayed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 */
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000253 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 public void setView(View view) {
255 mNextView = view;
256 }
257
258 /**
259 * Return the view.
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000260 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 * @see #setView
Bernardo Rufino7b843ab2020-01-17 19:15:36 +0000262 * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
263 * {@link #makeText(Context, CharSequence, int)} method, or use a
264 * <a href="{@docRoot}reference/com/google/android/material/snackbar/Snackbar">Snackbar</a>
265 * when in the foreground. Starting from Android {@link Build.VERSION_CODES#R}, apps
266 * targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background
267 * will not have custom toast views displayed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 */
269 public View getView() {
270 return mNextView;
271 }
272
273 /**
274 * Set how long to show the view for.
275 * @see #LENGTH_SHORT
276 * @see #LENGTH_LONG
277 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700278 public void setDuration(@Duration int duration) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 mDuration = duration;
Robert Carr70f0d222016-04-10 16:33:08 -0700280 mTN.mDuration = duration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
282
283 /**
284 * Return the duration.
285 * @see #setDuration
286 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700287 @Duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 public int getDuration() {
289 return mDuration;
290 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 /**
293 * Set the margins of the view.
294 *
295 * @param horizontalMargin The horizontal margin, in percentage of the
296 * container width, between the container's edges and the
297 * notification
298 * @param verticalMargin The vertical margin, in percentage of the
299 * container height, between the container's edges and the
300 * notification
301 */
302 public void setMargin(float horizontalMargin, float verticalMargin) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800303 mTN.mHorizontalMargin = horizontalMargin;
304 mTN.mVerticalMargin = verticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
306
307 /**
308 * Return the horizontal margin.
309 */
310 public float getHorizontalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800311 return mTN.mHorizontalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313
314 /**
315 * Return the vertical margin.
316 */
317 public float getVerticalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800318 return mTN.mVerticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
320
321 /**
322 * Set the location at which the notification should appear on the screen.
323 * @see android.view.Gravity
324 * @see #getGravity
325 */
326 public void setGravity(int gravity, int xOffset, int yOffset) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800327 mTN.mGravity = gravity;
328 mTN.mX = xOffset;
329 mTN.mY = yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331
332 /**
333 * Get the location at which the notification should appear on the screen.
334 * @see android.view.Gravity
335 * @see #getGravity
336 */
337 public int getGravity() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800338 return mTN.mGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 }
340
341 /**
342 * Return the X offset in pixels to apply to the gravity's location.
343 */
344 public int getXOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800345 return mTN.mX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400347
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 /**
349 * Return the Y offset in pixels to apply to the gravity's location.
350 */
351 public int getYOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800352 return mTN.mY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
Jason Monka5852682014-10-23 11:48:36 -0400354
355 /**
Bernardo Rufino3231f612019-12-20 18:04:48 +0000356 * Adds a callback to be notified when the toast is shown or hidden.
357 *
358 * Note that if the toast is blocked for some reason you won't get a call back.
359 *
360 * @see #removeCallback(Callback)
361 */
362 public void addCallback(@NonNull Callback callback) {
363 checkNotNull(callback);
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000364 synchronized (mCallbacks) {
365 mCallbacks.add(callback);
Bernardo Rufino3231f612019-12-20 18:04:48 +0000366 }
367 }
368
369 /**
370 * Removes a callback previously added with {@link #addCallback(Callback)}.
371 */
372 public void removeCallback(@NonNull Callback callback) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000373 synchronized (mCallbacks) {
374 mCallbacks.remove(callback);
Bernardo Rufino3231f612019-12-20 18:04:48 +0000375 }
376 }
377
378 /**
Jason Monka5852682014-10-23 11:48:36 -0400379 * Gets the LayoutParams for the Toast window.
380 * @hide
381 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100382 @UnsupportedAppUsage
Jason Monka5852682014-10-23 11:48:36 -0400383 public WindowManager.LayoutParams getWindowParams() {
384 return mTN.mParams;
385 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400386
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 /**
388 * Make a standard toast that just contains a text view.
389 *
390 * @param context The context to use. Usually your {@link android.app.Application}
391 * or {@link android.app.Activity} object.
392 * @param text The text to show. Can be formatted text.
393 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
394 * {@link #LENGTH_LONG}
395 *
396 */
Tor Norbyed9273d62013-05-30 15:59:53 -0700397 public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400398 return makeText(context, null, text, duration);
399 }
400
401 /**
402 * Make a standard toast to display using the specified looper.
403 * If looper is null, Looper.myLooper() is used.
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000404 *
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400405 * @hide
406 */
407 public static Toast makeText(@NonNull Context context, @Nullable Looper looper,
408 @NonNull CharSequence text, @Duration int duration) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000409 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
410 Toast result = new Toast(context, looper);
411 result.mText = text;
412 result.mDuration = duration;
413 return result;
414 } else {
415 Toast result = new Toast(context, looper);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000417 LayoutInflater inflate = (LayoutInflater)
418 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
419 View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
420 TextView tv = (TextView) v.findViewById(com.android.internal.R.id.message);
421 tv.setText(text);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400422
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000423 result.mNextView = v;
424 result.mDuration = duration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000426 return result;
427 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
429
430 /**
431 * Make a standard toast that just contains a text view with the text from a resource.
432 *
433 * @param context The context to use. Usually your {@link android.app.Application}
434 * or {@link android.app.Activity} object.
435 * @param resId The resource id of the string resource to use. Can be formatted text.
436 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
437 * {@link #LENGTH_LONG}
438 *
439 * @throws Resources.NotFoundException if the resource can't be found.
440 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700441 public static Toast makeText(Context context, @StringRes int resId, @Duration int duration)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 throws Resources.NotFoundException {
443 return makeText(context, context.getResources().getText(resId), duration);
444 }
445
446 /**
447 * Update the text in a Toast that was previously created using one of the makeText() methods.
448 * @param resId The new text for the Toast.
449 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700450 public void setText(@StringRes int resId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 setText(mContext.getText(resId));
452 }
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400453
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 /**
455 * Update the text in a Toast that was previously created using one of the makeText() methods.
456 * @param s The new text for the Toast.
457 */
458 public void setText(CharSequence s) {
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000459 if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
460 if (mNextView != null) {
461 throw new IllegalStateException(
462 "Text provided for custom toast, remove previous setView() calls if you "
463 + "want a text toast instead.");
464 }
465 mText = s;
466 } else {
467 if (mNextView == null) {
468 throw new RuntimeException("This Toast was not created with Toast.makeText()");
469 }
470 TextView tv = mNextView.findViewById(com.android.internal.R.id.message);
471 if (tv == null) {
472 throw new RuntimeException("This Toast was not created with Toast.makeText()");
473 }
474 tv.setText(s);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 // =======================================================================================
479 // All the gunk below is the interaction with the Notification Service, which handles
480 // the proper ordering of these system-wide.
481 // =======================================================================================
482
Adam Powellc7ac9f02019-02-04 13:29:11 -0800483 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 private static INotificationManager sService;
485
Adam Powellc7ac9f02019-02-04 13:29:11 -0800486 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guy0420cc72009-07-08 11:59:00 -0700487 static private INotificationManager getService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 if (sService != null) {
489 return sService;
490 }
491 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
492 return sService;
493 }
494
Romain Guyc4d09f22011-01-19 10:55:19 -0800495 private static class TN extends ITransientNotification.Stub {
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800496 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guy0420cc72009-07-08 11:59:00 -0700497 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
Robert Carr6cfc4e32016-10-05 15:33:16 -0700498
499 private static final int SHOW = 0;
500 private static final int HIDE = 1;
501 private static final int CANCEL = 2;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400502 final Handler mHandler;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800503
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800504 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Jose Lima08bef372013-08-08 11:51:00 -0700505 int mGravity;
Mathew Inwood74e7aed2018-08-16 17:29:31 +0100506 int mX;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800507 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Mathew Inwood74e7aed2018-08-16 17:29:31 +0100508 int mY;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800509 float mHorizontalMargin;
510 float mVerticalMargin;
511
Jose Lima08bef372013-08-08 11:51:00 -0700512
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800513 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guycf3dd6b2011-01-19 16:54:13 -0800514 View mView;
Daniel Santiago Riverae2429072019-02-04 15:19:14 -0800515 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Romain Guycf3dd6b2011-01-19 16:54:13 -0800516 View mNextView;
Robert Carr70f0d222016-04-10 16:33:08 -0700517 int mDuration;
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700518
Jeff Brown98365d72012-08-19 20:30:52 -0700519 WindowManager mWM;
Romain Guy0420cc72009-07-08 11:59:00 -0700520
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000521 final String mPackageName;
522 final Binder mToken;
Robert Carr6cfc4e32016-10-05 15:33:16 -0700523
Bernardo Rufino3231f612019-12-20 18:04:48 +0000524 @GuardedBy("mCallbacks")
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000525 private final List<Callback> mCallbacks;
Bernardo Rufino3231f612019-12-20 18:04:48 +0000526
Robert Carr017dbf52016-07-21 18:16:00 -0700527 static final long SHORT_DURATION_TIMEOUT = 4000;
528 static final long LONG_DURATION_TIMEOUT = 7000;
Robert Carr70f0d222016-04-10 16:33:08 -0700529
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000530 /**
531 * Creates a {@link ITransientNotification} object.
532 *
533 * The parameter {@code callbacks} is not copied and is accessed with itself as its own
534 * lock.
535 */
536 TN(String packageName, Binder token, List<Callback> callbacks, @Nullable Looper looper) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 // XXX This should be changed to use a Dialog, with a Theme.Toast
538 // defined that sets up the layout params appropriately.
Romain Guy0420cc72009-07-08 11:59:00 -0700539 final WindowManager.LayoutParams params = mParams;
540 params.height = WindowManager.LayoutParams.WRAP_CONTENT;
541 params.width = WindowManager.LayoutParams.WRAP_CONTENT;
Romain Guy0420cc72009-07-08 11:59:00 -0700542 params.format = PixelFormat.TRANSLUCENT;
543 params.windowAnimations = com.android.internal.R.style.Animation_Toast;
544 params.type = WindowManager.LayoutParams.TYPE_TOAST;
Tiger Huang52724442020-01-20 21:38:42 +0800545 params.setFitInsetsIgnoringVisibility(true);
Romain Guy0420cc72009-07-08 11:59:00 -0700546 params.setTitle("Toast");
John Spurlockf1a36642013-10-12 17:50:42 -0400547 params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
John Spurlockdcf4f212013-05-21 17:19:53 -0400548 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
John Spurlockf1a36642013-10-12 17:50:42 -0400549 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
Robert Carr6cfc4e32016-10-05 15:33:16 -0700550
551 mPackageName = packageName;
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000552 mToken = token;
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000553 mCallbacks = callbacks;
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400554
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400555 mHandler = new Handler(looper, null) {
556 @Override
557 public void handleMessage(Message msg) {
558 switch (msg.what) {
559 case SHOW: {
560 IBinder token = (IBinder) msg.obj;
561 handleShow(token);
562 break;
563 }
564 case HIDE: {
565 handleHide();
566 // Don't do this in handleHide() because it is also invoked by
567 // handleShow()
568 mNextView = null;
569 break;
570 }
571 case CANCEL: {
572 handleHide();
573 // Don't do this in handleHide() because it is also invoked by
574 // handleShow()
575 mNextView = null;
576 try {
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000577 getService().cancelToast(mPackageName, mToken);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400578 } catch (RemoteException e) {
579 }
580 break;
581 }
582 }
583 }
584 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 }
586
Bernardo Rufino3231f612019-12-20 18:04:48 +0000587 private List<Callback> getCallbacks() {
588 synchronized (mCallbacks) {
589 return new ArrayList<>(mCallbacks);
590 }
591 }
592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 /**
594 * schedule handleShow into the right thread
595 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700596 @Override
Mathew Inwood8c854f82018-09-14 12:35:36 +0100597 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700598 public void show(IBinder windowToken) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 if (localLOGV) Log.v(TAG, "SHOW: " + this);
Robert Carr6cfc4e32016-10-05 15:33:16 -0700600 mHandler.obtainMessage(SHOW, windowToken).sendToTarget();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602
603 /**
604 * schedule handleHide into the right thread
605 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700606 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700607 public void hide() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 if (localLOGV) Log.v(TAG, "HIDE: " + this);
Robert Carr6cfc4e32016-10-05 15:33:16 -0700609 mHandler.obtainMessage(HIDE).sendToTarget();
610 }
611
612 public void cancel() {
613 if (localLOGV) Log.v(TAG, "CANCEL: " + this);
614 mHandler.obtainMessage(CANCEL).sendToTarget();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 }
616
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700617 public void handleShow(IBinder windowToken) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800618 if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
619 + " mNextView=" + mNextView);
Svet Ganov0df37022017-05-15 23:45:03 -0700620 // If a cancel/hide is pending - no need to show - at this point
621 // the window token is already invalid and no need to do any work.
622 if (mHandler.hasMessages(CANCEL) || mHandler.hasMessages(HIDE)) {
623 return;
624 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800625 if (mView != mNextView) {
626 // remove the old view if necessary
627 handleHide();
628 mView = mNextView;
Craig Mautner54ae2f32012-12-05 09:49:57 -0800629 Context context = mView.getContext().getApplicationContext();
Tony Hill929442d2014-09-16 11:27:35 +0100630 String packageName = mView.getContext().getOpPackageName();
Craig Mautner54ae2f32012-12-05 09:49:57 -0800631 if (context == null) {
632 context = mView.getContext();
633 }
634 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Fabrice Di Megliob4946342012-06-07 16:29:18 -0700635 // We can resolve the Gravity here by using the Locale for getting
636 // the layout direction
Fabrice Di Meglio025f9602012-07-30 18:53:09 -0700637 final Configuration config = mView.getContext().getResources().getConfiguration();
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700638 final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
Romain Guycf3dd6b2011-01-19 16:54:13 -0800639 mParams.gravity = gravity;
640 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
641 mParams.horizontalWeight = 1.0f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800643 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
644 mParams.verticalWeight = 1.0f;
645 }
646 mParams.x = mX;
647 mParams.y = mY;
648 mParams.verticalMargin = mVerticalMargin;
649 mParams.horizontalMargin = mHorizontalMargin;
Tony Hill929442d2014-09-16 11:27:35 +0100650 mParams.packageName = packageName;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700651 mParams.hideTimeoutMilliseconds = mDuration ==
Robert Carr70f0d222016-04-10 16:33:08 -0700652 Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
Svetoslav Ganovaa076532016-08-01 19:16:43 -0700653 mParams.token = windowToken;
Romain Guycf3dd6b2011-01-19 16:54:13 -0800654 if (mView.getParent() != null) {
655 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
656 mWM.removeView(mView);
657 }
658 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
Svet Ganov0df37022017-05-15 23:45:03 -0700659 // Since the notification manager service cancels the token right
660 // after it notifies us to cancel the toast there is an inherent
661 // race and we may attempt to add a window after the token has been
662 // invalidated. Let us hedge against that.
663 try {
664 mWM.addView(mView, mParams);
665 trySendAccessibilityEvent();
Bernardo Rufino3231f612019-12-20 18:04:48 +0000666 for (Callback callback : getCallbacks()) {
667 callback.onToastShown();
668 }
Svet Ganov0df37022017-05-15 23:45:03 -0700669 } catch (WindowManager.BadTokenException e) {
670 /* ignore */
671 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 }
673 }
674
Romain Guycf3dd6b2011-01-19 16:54:13 -0800675 private void trySendAccessibilityEvent() {
676 AccessibilityManager accessibilityManager =
677 AccessibilityManager.getInstance(mView.getContext());
Eugene Suslad4128ec2017-12-04 19:48:41 +0000678 if (!accessibilityManager.isEnabled()) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800679 return;
680 }
681 // treat toasts as notifications since they are used to
682 // announce a transient piece of information to the user
683 AccessibilityEvent event = AccessibilityEvent.obtain(
684 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
685 event.setClassName(getClass().getName());
686 event.setPackageName(mView.getContext().getPackageName());
687 mView.dispatchPopulateAccessibilityEvent(event);
688 accessibilityManager.sendAccessibilityEvent(event);
Geoffrey Pitschd34c1872017-05-04 16:02:15 -0400689 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800690
Mathew Inwood978c6e22018-08-21 15:58:55 +0100691 @UnsupportedAppUsage
Romain Guy0420cc72009-07-08 11:59:00 -0700692 public void handleHide() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800693 if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
694 if (mView != null) {
695 // note: checking parent() just to make sure the view has
696 // been added... i have seen cases where we get here when
697 // the view isn't yet added, so let's try not to crash.
698 if (mView.getParent() != null) {
699 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
Svet Ganov1777c912016-09-08 07:11:52 -0700700 mWM.removeViewImmediate(mView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800702
Robert Carr997427342018-02-28 18:06:10 -0800703
704 // Now that we've removed the view it's safe for the server to release
705 // the resources.
706 try {
Bernardo Rufino52af6db2019-12-20 18:26:07 +0000707 getService().finishToken(mPackageName, mToken);
Robert Carr997427342018-02-28 18:06:10 -0800708 } catch (RemoteException e) {
709 }
710
Bernardo Rufino3231f612019-12-20 18:04:48 +0000711 for (Callback callback : getCallbacks()) {
712 callback.onToastHidden();
713 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800714 mView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 }
716 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 }
Bernardo Rufino3231f612019-12-20 18:04:48 +0000718
719 /**
720 * Callback object to be called when the toast is shown or hidden.
721 *
722 * Callback methods will be called on the looper thread provided on construction.
723 *
724 * @see #addCallback(Callback)
725 */
726 public abstract static class Callback {
727 /**
728 * Called when the toast is displayed on the screen.
729 */
730 public void onToastShown() {}
731
732 /**
733 * Called when the toast is hidden.
734 */
735 public void onToastHidden() {}
736 }
Bernardo Rufinoab338ac2019-12-20 18:35:30 +0000737
738 private static class CallbackBinder extends ITransientNotificationCallback.Stub {
739 private final Handler mHandler;
740
741 @GuardedBy("mCallbacks")
742 private final List<Callback> mCallbacks;
743
744 /**
745 * Creates a {@link ITransientNotificationCallback} object.
746 *
747 * The parameter {@code callbacks} is not copied and is accessed with itself as its own
748 * lock.
749 */
750 private CallbackBinder(List<Callback> callbacks, Handler handler) {
751 mCallbacks = callbacks;
752 mHandler = handler;
753 }
754
755 @Override
756 public void onToastShown() {
757 mHandler.post(() -> {
758 for (Callback callback : getCallbacks()) {
759 callback.onToastShown();
760 }
761 });
762 }
763
764 @Override
765 public void onToastHidden() {
766 mHandler.post(() -> {
767 for (Callback callback : getCallbacks()) {
768 callback.onToastHidden();
769 }
770 });
771 }
772
773 private List<Callback> getCallbacks() {
774 synchronized (mCallbacks) {
775 return new ArrayList<>(mCallbacks);
776 }
777 }
778 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779}