blob: aaa1adaa7e639d7b5cf4263450511e5ba2404bc6 [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
19import android.app.INotificationManager;
20import android.app.ITransientNotification;
21import android.content.Context;
Fabrice Di Meglio025f9602012-07-30 18:53:09 -070022import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.res.Resources;
24import android.graphics.PixelFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.os.Handler;
svetoslavganov75986cf2009-05-14 22:28:01 -070026import android.os.RemoteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.ServiceManager;
28import android.util.Log;
29import android.view.Gravity;
30import android.view.LayoutInflater;
31import android.view.View;
John Spurlockdcf4f212013-05-21 17:19:53 -040032import android.view.View.OnClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import 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
37/**
38 * A toast is a view containing a quick little message for the user. The toast class
39 * helps you create and show those.
40 * {@more}
41 *
42 * <p>
43 * When the view is shown to the user, appears as a floating view over the
44 * application. It will never receive focus. The user will probably be in the
45 * middle of typing something else. The idea is to be as unobtrusive as
46 * possible, while still showing the user the information you want them to see.
47 * Two examples are the volume control, and the brief message saying that your
48 * settings have been saved.
49 * <p>
50 * The easiest way to use this class is to call one of the static methods that constructs
51 * everything you need and returns a new Toast object.
Joe Fernandez558459f2011-10-13 16:47:36 -070052 *
53 * <div class="special reference">
54 * <h3>Developer Guides</h3>
55 * <p>For information about creating Toast notifications, read the
56 * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer
57 * guide.</p>
58 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 */
60public class Toast {
61 static final String TAG = "Toast";
62 static final boolean localLOGV = false;
63
64 /**
65 * Show the view or text notification for a short period of time. This time
66 * could be user-definable. This is the default.
67 * @see #setDuration
68 */
69 public static final int LENGTH_SHORT = 0;
70
71 /**
72 * Show the view or text notification for a long period of time. This time
73 * could be user-definable.
74 * @see #setDuration
75 */
76 public static final int LENGTH_LONG = 1;
77
John Spurlockdcf4f212013-05-21 17:19:53 -040078 /** @hide */
79 public static final int LENGTH_INFINITE = 2;
80
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 final Context mContext;
82 final TN mTN;
83 int mDuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 View mNextView;
85
86 /**
87 * Construct an empty Toast object. You must call {@link #setView} before you
88 * can call {@link #show}.
89 *
90 * @param context The context to use. Usually your {@link android.app.Application}
91 * or {@link android.app.Activity} object.
92 */
93 public Toast(Context context) {
94 mContext = context;
Romain Guycf3dd6b2011-01-19 16:54:13 -080095 mTN = new TN();
96 mTN.mY = context.getResources().getDimensionPixelSize(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 com.android.internal.R.dimen.toast_y_offset);
98 }
99
100 /**
101 * Show the view for the specified duration.
102 */
103 public void show() {
104 if (mNextView == null) {
105 throw new RuntimeException("setView must have been called");
106 }
107
108 INotificationManager service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 String pkg = mContext.getPackageName();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800110 TN tn = mTN;
111 tn.mNextView = mNextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 try {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800114 service.enqueueToast(pkg, tn, mDuration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 } catch (RemoteException e) {
116 // Empty
117 }
118 }
119
120 /**
121 * Close the view if it's showing, or don't show it if it isn't showing yet.
122 * You do not normally have to call this. Normally view will disappear on its own
123 * after the appropriate duration.
124 */
125 public void cancel() {
126 mTN.hide();
Jeff Brown51f24302012-05-20 12:07:26 -0700127
128 try {
129 getService().cancelToast(mContext.getPackageName(), mTN);
130 } catch (RemoteException e) {
131 // Empty
132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 }
134
135 /**
136 * Set the view to show.
137 * @see #getView
138 */
139 public void setView(View view) {
140 mNextView = view;
141 }
142
143 /**
144 * Return the view.
145 * @see #setView
146 */
147 public View getView() {
148 return mNextView;
149 }
150
151 /**
152 * Set how long to show the view for.
153 * @see #LENGTH_SHORT
154 * @see #LENGTH_LONG
155 */
156 public void setDuration(int duration) {
157 mDuration = duration;
158 }
159
160 /**
161 * Return the duration.
162 * @see #setDuration
163 */
164 public int getDuration() {
165 return mDuration;
166 }
167
168 /**
169 * Set the margins of the view.
170 *
171 * @param horizontalMargin The horizontal margin, in percentage of the
172 * container width, between the container's edges and the
173 * notification
174 * @param verticalMargin The vertical margin, in percentage of the
175 * container height, between the container's edges and the
176 * notification
177 */
178 public void setMargin(float horizontalMargin, float verticalMargin) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800179 mTN.mHorizontalMargin = horizontalMargin;
180 mTN.mVerticalMargin = verticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 }
182
183 /**
184 * Return the horizontal margin.
185 */
186 public float getHorizontalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800187 return mTN.mHorizontalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 }
189
190 /**
191 * Return the vertical margin.
192 */
193 public float getVerticalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800194 return mTN.mVerticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 }
196
197 /**
198 * Set the location at which the notification should appear on the screen.
199 * @see android.view.Gravity
200 * @see #getGravity
201 */
202 public void setGravity(int gravity, int xOffset, int yOffset) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800203 mTN.mGravity = gravity;
204 mTN.mX = xOffset;
205 mTN.mY = yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
207
208 /**
209 * Get the location at which the notification should appear on the screen.
210 * @see android.view.Gravity
211 * @see #getGravity
212 */
213 public int getGravity() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800214 return mTN.mGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
216
217 /**
218 * Return the X offset in pixels to apply to the gravity's location.
219 */
220 public int getXOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800221 return mTN.mX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223
224 /**
225 * Return the Y offset in pixels to apply to the gravity's location.
226 */
227 public int getYOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800228 return mTN.mY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 }
230
231 /**
232 * Make a standard toast that just contains a text view.
233 *
234 * @param context The context to use. Usually your {@link android.app.Application}
235 * or {@link android.app.Activity} object.
236 * @param text The text to show. Can be formatted text.
237 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
238 * {@link #LENGTH_LONG}
239 *
240 */
241 public static Toast makeText(Context context, CharSequence text, int duration) {
242 Toast result = new Toast(context);
243
Romain Guy0420cc72009-07-08 11:59:00 -0700244 LayoutInflater inflate = (LayoutInflater)
245 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
247 TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
248 tv.setText(text);
249
250 result.mNextView = v;
251 result.mDuration = duration;
252
253 return result;
254 }
255
256 /**
257 * Make a standard toast that just contains a text view with the text from a resource.
258 *
259 * @param context The context to use. Usually your {@link android.app.Application}
260 * or {@link android.app.Activity} object.
261 * @param resId The resource id of the string resource to use. Can be formatted text.
262 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
263 * {@link #LENGTH_LONG}
264 *
265 * @throws Resources.NotFoundException if the resource can't be found.
266 */
267 public static Toast makeText(Context context, int resId, int duration)
268 throws Resources.NotFoundException {
269 return makeText(context, context.getResources().getText(resId), duration);
270 }
271
272 /**
273 * Update the text in a Toast that was previously created using one of the makeText() methods.
274 * @param resId The new text for the Toast.
275 */
276 public void setText(int resId) {
277 setText(mContext.getText(resId));
278 }
279
280 /**
281 * Update the text in a Toast that was previously created using one of the makeText() methods.
282 * @param s The new text for the Toast.
283 */
284 public void setText(CharSequence s) {
285 if (mNextView == null) {
286 throw new RuntimeException("This Toast was not created with Toast.makeText()");
287 }
288 TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
289 if (tv == null) {
290 throw new RuntimeException("This Toast was not created with Toast.makeText()");
291 }
292 tv.setText(s);
293 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700294
John Spurlockdcf4f212013-05-21 17:19:53 -0400295 /** @hide */
296 public static Toast makeBar(Context context, int resId, int duration) {
297 return makeBar(context, context.getResources().getText(resId), duration);
298 }
299
300 /** @hide */
301 public static Toast makeBar(Context context, CharSequence text, int duration) {
302 Toast result = new Toast(context);
303
304 LayoutInflater inflate = (LayoutInflater)
305 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
306 View v = inflate.inflate(com.android.internal.R.layout.toast_bar, null);
307 ((TextView)v.findViewById(android.R.id.message)).setText(text);
308 v.findViewById(android.R.id.button1).setVisibility(View.GONE);
309
310 result.mNextView = v;
311 result.mDuration = duration;
312 result.mTN.mParams.alpha = 0.9f;
313 result.mTN.mParams.windowAnimations = com.android.internal.R.style.Animation_ToastBar;
314
315 return result;
316 }
317
318 /** @hide */
319 public Toast setAction(int resId, Runnable action) {
320 return setAction(mContext.getResources().getText(resId), action);
321 }
322
323 /** @hide */
324 public Toast setAction(CharSequence actionText, final Runnable action) {
325 if (mNextView != null) {
326 TextView text1 = (TextView)mNextView.findViewById(android.R.id.text1);
327 View button1 = mNextView.findViewById(android.R.id.button1);
328 if (text1 != null && button1 != null) {
329 text1.setText(actionText);
330 button1.setVisibility(View.VISIBLE);
331 button1.setOnClickListener(new OnClickListener() {
332 @Override
333 public void onClick(View v) {
334 if (action != null) {
335 action.run();
336 }
337 }});
338 return setInteractive(true);
339 }
340 }
341 throw new RuntimeException("This Toast was not created with Toast.makeBar()");
342 }
343
344 /** @hide */
345 public Toast setInteractive(boolean interactive) {
346 mTN.setInteractive(interactive);
347 return this;
348 }
349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 // =======================================================================================
351 // All the gunk below is the interaction with the Notification Service, which handles
352 // the proper ordering of these system-wide.
353 // =======================================================================================
354
355 private static INotificationManager sService;
356
Romain Guy0420cc72009-07-08 11:59:00 -0700357 static private INotificationManager getService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 if (sService != null) {
359 return sService;
360 }
361 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
362 return sService;
363 }
364
Romain Guyc4d09f22011-01-19 10:55:19 -0800365 private static class TN extends ITransientNotification.Stub {
Romain Guy0420cc72009-07-08 11:59:00 -0700366 final Runnable mShow = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700367 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700368 public void run() {
369 handleShow();
370 }
371 };
372
373 final Runnable mHide = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700374 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700375 public void run() {
376 handleHide();
Romain Guy2152ca52011-01-21 12:31:53 -0800377 // Don't do this in handleHide() because it is also invoked by handleShow()
378 mNextView = null;
Romain Guy0420cc72009-07-08 11:59:00 -0700379 }
380 };
381
382 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800383 final Handler mHandler = new Handler();
384
385 int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
386 int mX, mY;
387 float mHorizontalMargin;
388 float mVerticalMargin;
389
390
391 View mView;
392 View mNextView;
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700393
Jeff Brown98365d72012-08-19 20:30:52 -0700394 WindowManager mWM;
Romain Guy0420cc72009-07-08 11:59:00 -0700395
Romain Guycf3dd6b2011-01-19 16:54:13 -0800396 TN() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 // XXX This should be changed to use a Dialog, with a Theme.Toast
398 // defined that sets up the layout params appropriately.
Romain Guy0420cc72009-07-08 11:59:00 -0700399 final WindowManager.LayoutParams params = mParams;
400 params.height = WindowManager.LayoutParams.WRAP_CONTENT;
401 params.width = WindowManager.LayoutParams.WRAP_CONTENT;
Romain Guy0420cc72009-07-08 11:59:00 -0700402 params.format = PixelFormat.TRANSLUCENT;
403 params.windowAnimations = com.android.internal.R.style.Animation_Toast;
404 params.type = WindowManager.LayoutParams.TYPE_TOAST;
405 params.setTitle("Toast");
John Spurlockdcf4f212013-05-21 17:19:53 -0400406 setInteractive(false);
407 }
408
409 private void setInteractive(boolean interactive) {
410 mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
411 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
412 | (interactive
413 ? (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
414 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH)
415 : WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 }
417
418 /**
419 * schedule handleShow into the right thread
420 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700421 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700422 public void show() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 if (localLOGV) Log.v(TAG, "SHOW: " + this);
424 mHandler.post(mShow);
425 }
426
427 /**
428 * schedule handleHide into the right thread
429 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700430 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700431 public void hide() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 if (localLOGV) Log.v(TAG, "HIDE: " + this);
433 mHandler.post(mHide);
434 }
435
Romain Guy0420cc72009-07-08 11:59:00 -0700436 public void handleShow() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800437 if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
438 + " mNextView=" + mNextView);
439 if (mView != mNextView) {
440 // remove the old view if necessary
441 handleHide();
442 mView = mNextView;
Craig Mautner54ae2f32012-12-05 09:49:57 -0800443 Context context = mView.getContext().getApplicationContext();
444 if (context == null) {
445 context = mView.getContext();
446 }
447 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Fabrice Di Megliob4946342012-06-07 16:29:18 -0700448 // We can resolve the Gravity here by using the Locale for getting
449 // the layout direction
Fabrice Di Meglio025f9602012-07-30 18:53:09 -0700450 final Configuration config = mView.getContext().getResources().getConfiguration();
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700451 final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
Romain Guycf3dd6b2011-01-19 16:54:13 -0800452 mParams.gravity = gravity;
453 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
454 mParams.horizontalWeight = 1.0f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800456 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
457 mParams.verticalWeight = 1.0f;
458 }
459 mParams.x = mX;
460 mParams.y = mY;
461 mParams.verticalMargin = mVerticalMargin;
462 mParams.horizontalMargin = mHorizontalMargin;
463 if (mView.getParent() != null) {
464 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
465 mWM.removeView(mView);
466 }
467 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
468 mWM.addView(mView, mParams);
469 trySendAccessibilityEvent();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 }
471 }
472
Romain Guycf3dd6b2011-01-19 16:54:13 -0800473 private void trySendAccessibilityEvent() {
474 AccessibilityManager accessibilityManager =
475 AccessibilityManager.getInstance(mView.getContext());
476 if (!accessibilityManager.isEnabled()) {
477 return;
478 }
479 // treat toasts as notifications since they are used to
480 // announce a transient piece of information to the user
481 AccessibilityEvent event = AccessibilityEvent.obtain(
482 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
483 event.setClassName(getClass().getName());
484 event.setPackageName(mView.getContext().getPackageName());
485 mView.dispatchPopulateAccessibilityEvent(event);
486 accessibilityManager.sendAccessibilityEvent(event);
487 }
488
Romain Guy0420cc72009-07-08 11:59:00 -0700489 public void handleHide() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800490 if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
491 if (mView != null) {
492 // note: checking parent() just to make sure the view has
493 // been added... i have seen cases where we get here when
494 // the view isn't yet added, so let's try not to crash.
495 if (mView.getParent() != null) {
496 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
497 mWM.removeView(mView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800499
500 mView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 }
502 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504}