blob: 1d85126ecb42cf75f0939333db761154a27b0cbf [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;
32import android.view.WindowManager;
Svetoslav Ganovcd2613a2010-10-05 15:55:39 -070033import android.view.accessibility.AccessibilityEvent;
34import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36/**
37 * A toast is a view containing a quick little message for the user. The toast class
38 * helps you create and show those.
39 * {@more}
40 *
41 * <p>
42 * When the view is shown to the user, appears as a floating view over the
43 * application. It will never receive focus. The user will probably be in the
44 * middle of typing something else. The idea is to be as unobtrusive as
45 * possible, while still showing the user the information you want them to see.
46 * Two examples are the volume control, and the brief message saying that your
47 * settings have been saved.
48 * <p>
49 * The easiest way to use this class is to call one of the static methods that constructs
50 * everything you need and returns a new Toast object.
Joe Fernandez558459f2011-10-13 16:47:36 -070051 *
52 * <div class="special reference">
53 * <h3>Developer Guides</h3>
54 * <p>For information about creating Toast notifications, read the
55 * <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Toast Notifications</a> developer
56 * guide.</p>
57 * </div>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 */
59public class Toast {
60 static final String TAG = "Toast";
61 static final boolean localLOGV = false;
62
63 /**
64 * Show the view or text notification for a short period of time. This time
65 * could be user-definable. This is the default.
66 * @see #setDuration
67 */
68 public static final int LENGTH_SHORT = 0;
69
70 /**
71 * Show the view or text notification for a long period of time. This time
72 * could be user-definable.
73 * @see #setDuration
74 */
75 public static final int LENGTH_LONG = 1;
76
77 final Context mContext;
78 final TN mTN;
79 int mDuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 View mNextView;
81
82 /**
83 * Construct an empty Toast object. You must call {@link #setView} before you
84 * can call {@link #show}.
85 *
86 * @param context The context to use. Usually your {@link android.app.Application}
87 * or {@link android.app.Activity} object.
88 */
89 public Toast(Context context) {
90 mContext = context;
Romain Guycf3dd6b2011-01-19 16:54:13 -080091 mTN = new TN();
92 mTN.mY = context.getResources().getDimensionPixelSize(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 com.android.internal.R.dimen.toast_y_offset);
94 }
95
96 /**
97 * Show the view for the specified duration.
98 */
99 public void show() {
100 if (mNextView == null) {
101 throw new RuntimeException("setView must have been called");
102 }
103
104 INotificationManager service = getService();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 String pkg = mContext.getPackageName();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800106 TN tn = mTN;
107 tn.mNextView = mNextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 try {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800110 service.enqueueToast(pkg, tn, mDuration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 } catch (RemoteException e) {
112 // Empty
113 }
114 }
115
116 /**
117 * Close the view if it's showing, or don't show it if it isn't showing yet.
118 * You do not normally have to call this. Normally view will disappear on its own
119 * after the appropriate duration.
120 */
121 public void cancel() {
122 mTN.hide();
Jeff Brown51f24302012-05-20 12:07:26 -0700123
124 try {
125 getService().cancelToast(mContext.getPackageName(), mTN);
126 } catch (RemoteException e) {
127 // Empty
128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 }
130
131 /**
132 * Set the view to show.
133 * @see #getView
134 */
135 public void setView(View view) {
136 mNextView = view;
137 }
138
139 /**
140 * Return the view.
141 * @see #setView
142 */
143 public View getView() {
144 return mNextView;
145 }
146
147 /**
148 * Set how long to show the view for.
149 * @see #LENGTH_SHORT
150 * @see #LENGTH_LONG
151 */
152 public void setDuration(int duration) {
153 mDuration = duration;
154 }
155
156 /**
157 * Return the duration.
158 * @see #setDuration
159 */
160 public int getDuration() {
161 return mDuration;
162 }
163
164 /**
165 * Set the margins of the view.
166 *
167 * @param horizontalMargin The horizontal margin, in percentage of the
168 * container width, between the container's edges and the
169 * notification
170 * @param verticalMargin The vertical margin, in percentage of the
171 * container height, between the container's edges and the
172 * notification
173 */
174 public void setMargin(float horizontalMargin, float verticalMargin) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800175 mTN.mHorizontalMargin = horizontalMargin;
176 mTN.mVerticalMargin = verticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 }
178
179 /**
180 * Return the horizontal margin.
181 */
182 public float getHorizontalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800183 return mTN.mHorizontalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 }
185
186 /**
187 * Return the vertical margin.
188 */
189 public float getVerticalMargin() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800190 return mTN.mVerticalMargin;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192
193 /**
194 * Set the location at which the notification should appear on the screen.
195 * @see android.view.Gravity
196 * @see #getGravity
197 */
198 public void setGravity(int gravity, int xOffset, int yOffset) {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800199 mTN.mGravity = gravity;
200 mTN.mX = xOffset;
201 mTN.mY = yOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 }
203
204 /**
205 * Get the location at which the notification should appear on the screen.
206 * @see android.view.Gravity
207 * @see #getGravity
208 */
209 public int getGravity() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800210 return mTN.mGravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212
213 /**
214 * Return the X offset in pixels to apply to the gravity's location.
215 */
216 public int getXOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800217 return mTN.mX;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219
220 /**
221 * Return the Y offset in pixels to apply to the gravity's location.
222 */
223 public int getYOffset() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800224 return mTN.mY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 }
226
227 /**
228 * Make a standard toast that just contains a text view.
229 *
230 * @param context The context to use. Usually your {@link android.app.Application}
231 * or {@link android.app.Activity} object.
232 * @param text The text to show. Can be formatted text.
233 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
234 * {@link #LENGTH_LONG}
235 *
236 */
237 public static Toast makeText(Context context, CharSequence text, int duration) {
238 Toast result = new Toast(context);
239
Romain Guy0420cc72009-07-08 11:59:00 -0700240 LayoutInflater inflate = (LayoutInflater)
241 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
243 TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
244 tv.setText(text);
245
246 result.mNextView = v;
247 result.mDuration = duration;
248
249 return result;
250 }
251
252 /**
253 * Make a standard toast that just contains a text view with the text from a resource.
254 *
255 * @param context The context to use. Usually your {@link android.app.Application}
256 * or {@link android.app.Activity} object.
257 * @param resId The resource id of the string resource to use. Can be formatted text.
258 * @param duration How long to display the message. Either {@link #LENGTH_SHORT} or
259 * {@link #LENGTH_LONG}
260 *
261 * @throws Resources.NotFoundException if the resource can't be found.
262 */
263 public static Toast makeText(Context context, int resId, int duration)
264 throws Resources.NotFoundException {
265 return makeText(context, context.getResources().getText(resId), duration);
266 }
267
268 /**
269 * Update the text in a Toast that was previously created using one of the makeText() methods.
270 * @param resId The new text for the Toast.
271 */
272 public void setText(int resId) {
273 setText(mContext.getText(resId));
274 }
275
276 /**
277 * Update the text in a Toast that was previously created using one of the makeText() methods.
278 * @param s The new text for the Toast.
279 */
280 public void setText(CharSequence s) {
281 if (mNextView == null) {
282 throw new RuntimeException("This Toast was not created with Toast.makeText()");
283 }
284 TextView tv = (TextView) mNextView.findViewById(com.android.internal.R.id.message);
285 if (tv == null) {
286 throw new RuntimeException("This Toast was not created with Toast.makeText()");
287 }
288 tv.setText(s);
289 }
svetoslavganov75986cf2009-05-14 22:28:01 -0700290
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 // =======================================================================================
292 // All the gunk below is the interaction with the Notification Service, which handles
293 // the proper ordering of these system-wide.
294 // =======================================================================================
295
296 private static INotificationManager sService;
297
Romain Guy0420cc72009-07-08 11:59:00 -0700298 static private INotificationManager getService() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 if (sService != null) {
300 return sService;
301 }
302 sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
303 return sService;
304 }
305
Romain Guyc4d09f22011-01-19 10:55:19 -0800306 private static class TN extends ITransientNotification.Stub {
Romain Guy0420cc72009-07-08 11:59:00 -0700307 final Runnable mShow = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700308 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700309 public void run() {
310 handleShow();
311 }
312 };
313
314 final Runnable mHide = new Runnable() {
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700315 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700316 public void run() {
317 handleHide();
Romain Guy2152ca52011-01-21 12:31:53 -0800318 // Don't do this in handleHide() because it is also invoked by handleShow()
319 mNextView = null;
Romain Guy0420cc72009-07-08 11:59:00 -0700320 }
321 };
322
323 private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
Romain Guycf3dd6b2011-01-19 16:54:13 -0800324 final Handler mHandler = new Handler();
325
326 int mGravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
327 int mX, mY;
328 float mHorizontalMargin;
329 float mVerticalMargin;
330
331
332 View mView;
333 View mNextView;
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700334
Jeff Brown98365d72012-08-19 20:30:52 -0700335 WindowManager mWM;
Romain Guy0420cc72009-07-08 11:59:00 -0700336
Romain Guycf3dd6b2011-01-19 16:54:13 -0800337 TN() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 // XXX This should be changed to use a Dialog, with a Theme.Toast
339 // defined that sets up the layout params appropriately.
Romain Guy0420cc72009-07-08 11:59:00 -0700340 final WindowManager.LayoutParams params = mParams;
341 params.height = WindowManager.LayoutParams.WRAP_CONTENT;
342 params.width = WindowManager.LayoutParams.WRAP_CONTENT;
343 params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
345 | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
Romain Guy0420cc72009-07-08 11:59:00 -0700346 params.format = PixelFormat.TRANSLUCENT;
347 params.windowAnimations = com.android.internal.R.style.Animation_Toast;
348 params.type = WindowManager.LayoutParams.TYPE_TOAST;
349 params.setTitle("Toast");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351
352 /**
353 * schedule handleShow into the right thread
354 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700355 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700356 public void show() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 if (localLOGV) Log.v(TAG, "SHOW: " + this);
358 mHandler.post(mShow);
359 }
360
361 /**
362 * schedule handleHide into the right thread
363 */
Craig Mautnerb6ee2a22012-08-27 16:55:04 -0700364 @Override
Romain Guy0420cc72009-07-08 11:59:00 -0700365 public void hide() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 if (localLOGV) Log.v(TAG, "HIDE: " + this);
367 mHandler.post(mHide);
368 }
369
Romain Guy0420cc72009-07-08 11:59:00 -0700370 public void handleShow() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800371 if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
372 + " mNextView=" + mNextView);
373 if (mView != mNextView) {
374 // remove the old view if necessary
375 handleHide();
376 mView = mNextView;
Craig Mautner54ae2f32012-12-05 09:49:57 -0800377 Context context = mView.getContext().getApplicationContext();
378 if (context == null) {
379 context = mView.getContext();
380 }
381 mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Fabrice Di Megliob4946342012-06-07 16:29:18 -0700382 // We can resolve the Gravity here by using the Locale for getting
383 // the layout direction
Fabrice Di Meglio025f9602012-07-30 18:53:09 -0700384 final Configuration config = mView.getContext().getResources().getConfiguration();
Fabrice Di Meglio5f797992012-06-15 20:16:41 -0700385 final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection());
Romain Guycf3dd6b2011-01-19 16:54:13 -0800386 mParams.gravity = gravity;
387 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
388 mParams.horizontalWeight = 1.0f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800390 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
391 mParams.verticalWeight = 1.0f;
392 }
393 mParams.x = mX;
394 mParams.y = mY;
395 mParams.verticalMargin = mVerticalMargin;
396 mParams.horizontalMargin = mHorizontalMargin;
397 if (mView.getParent() != null) {
398 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
399 mWM.removeView(mView);
400 }
401 if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
402 mWM.addView(mView, mParams);
403 trySendAccessibilityEvent();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 }
405 }
406
Romain Guycf3dd6b2011-01-19 16:54:13 -0800407 private void trySendAccessibilityEvent() {
408 AccessibilityManager accessibilityManager =
409 AccessibilityManager.getInstance(mView.getContext());
410 if (!accessibilityManager.isEnabled()) {
411 return;
412 }
413 // treat toasts as notifications since they are used to
414 // announce a transient piece of information to the user
415 AccessibilityEvent event = AccessibilityEvent.obtain(
416 AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
417 event.setClassName(getClass().getName());
418 event.setPackageName(mView.getContext().getPackageName());
419 mView.dispatchPopulateAccessibilityEvent(event);
420 accessibilityManager.sendAccessibilityEvent(event);
421 }
422
Romain Guy0420cc72009-07-08 11:59:00 -0700423 public void handleHide() {
Romain Guycf3dd6b2011-01-19 16:54:13 -0800424 if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
425 if (mView != null) {
426 // note: checking parent() just to make sure the view has
427 // been added... i have seen cases where we get here when
428 // the view isn't yet added, so let's try not to crash.
429 if (mView.getParent() != null) {
430 if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
431 mWM.removeView(mView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 }
Romain Guycf3dd6b2011-01-19 16:54:13 -0800433
434 mView = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 }
436 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438}