AI2 Component  (Version nb184)
Notifier.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
9 import android.app.Activity;
10 import android.app.AlertDialog;
11 import android.app.ProgressDialog;
12 import android.content.Context;
13 import android.content.DialogInterface;
14 import android.graphics.Color;
15 import android.graphics.Typeface;
16 import android.os.Handler;
17 import android.text.Html;
18 import android.text.InputType;
19 import android.text.SpannableString;
20 import android.util.Log;
21 import android.view.Gravity;
22 import android.view.inputmethod.InputMethodManager;
23 import android.widget.EditText;
24 import android.widget.TextView;
25 import android.widget.Toast;
26 import android.view.View;
27 
40 
48 @DesignerComponent(version = YaVersion.NOTIFIER_COMPONENT_VERSION,
49  category = ComponentCategory.USERINTERFACE,
50  description = "The Notifier component displays alert dialogs, messages, and temporary alerts, " +
51  "and creates Android log entries through the following methods: " +
52  "<ul>" +
53  "<li> ShowMessageDialog: displays a message which the user must dismiss by pressing a button.</li>" +
54  "<li> ShowChooseDialog: displays a message two buttons to let the user choose one of two responses, " +
55  "for example, yes or no, after which the AfterChoosing event is raised.</li>" +
56  "<li> ShowTextDialog: lets the user enter text in response to the message, after " +
57  "which the AfterTextInput event is raised. " +
58  "<li> ShowPasswordDialog: lets the user enter password in response to the message, after " +
59  "which the AfterTextInput event is raised. " +
60  "<li> ShowAlert: displays a temporary alert that goes away by itself after a short time.</li>" +
61  "<li> ShowProgressDialog: displays an alert with a loading spinner that cannot be dismissed by " +
62  "the user. It can only be dismissed by using the DismissProgressDialog block.</li>" +
63  "<li> DismissProgressDialog: Dismisses the progress dialog displayed by ShowProgressDialog.</li>" +
64  "<li> LogError: logs an error message to the Android log. </li>" +
65  "<li> LogInfo: logs an info message to the Android log.</li>" +
66  "<li> LogWarning: logs a warning message to the Android log.</li>" +
67  "<li>The messages in the dialogs (but not the alert) can be formatted using the following HTML tags:" +
68  "&lt;b&gt;, &lt;big&gt;, &lt;blockquote&gt;, &lt;br&gt;, &lt;cite&gt;, &lt;dfn&gt;, &lt;div&gt;, " +
69  "&lt;em&gt;, &lt;small&gt;, &lt;strong&gt;, &lt;sub&gt;, &lt;sup&gt;, &lt;tt&gt;. &lt;u&gt;</li>" +
70  "<li>You can also use the font tag to specify color, for example, &lt;font color=\"blue\"&gt;. Some of the " +
71  "available color names are aqua, black, blue, fuchsia, green, grey, lime, maroon, navy, olive, purple, " +
72  "red, silver, teal, white, and yellow</li>" +
73  "</ul>",
74  nonVisible = true,
75  iconName = "images/notifier.png")
76 @SimpleObject
77 
78 public final class Notifier extends AndroidNonvisibleComponent implements Component {
79 
80  private static final String LOG_TAG = "Notifier";
81  private final Activity activity;
82  private final Handler handler;
83  private ProgressDialog progressDialog;
84 
85  //Length of Notifier message display
86  private int notifierLength = Component.TOAST_LENGTH_LONG;
87 
88  // Notifier background color
89  private int backgroundColor = Color.DKGRAY;
90 
91  // Notifier text color
92  private int textColor = Color.WHITE;
93 
99  public Notifier (ComponentContainer container) {
100  super(container.$form());
101  activity = container.$context();
102  handler = new Handler();
103  progressDialog = null;
104  }
105 
115  @SimpleFunction(description = "Shows a dialog box with an optional title and message "
116  + "(use empty strings if they are not wanted). This dialog box contains a spinning "
117  + "artifact to indicate that the program is working. It cannot be canceled by the user "
118  + "but must be dismissed by the App Inventor Program by using the DismissProgressDialog "
119  + "block.")
120  public void ShowProgressDialog(String message, String title) {
121  progressDialog(message, title);
122  }
123 
127  @SimpleFunction(description = "Dismiss a previously displayed ProgressDialog box")
128  public void DismissProgressDialog() {
129  if (progressDialog != null) {
130  progressDialog.dismiss();
131  progressDialog = null;
132  }
133  }
134 
141  public void progressDialog(String message, String title) {
142  if (progressDialog != null) {
144  }
145  progressDialog = ProgressDialog.show(activity, title, message);
146  // prevents the user from escaping the dialog by hitting the Back button
147  progressDialog.setCancelable(false);
148  }
149 
158  public void ShowMessageDialog(String message, String title, String buttonText) {
159  oneButtonAlert(activity, message, title, buttonText);
160  }
161 
162  // This method is declared static, with an explicit activity input, so that other
163  // components can use it
164  public static void oneButtonAlert(Activity activity,String message, String title, String buttonText, final Runnable callBack) {
165  Log.i(LOG_TAG, "One button alert " + message);
166  AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
167  alertDialog.setTitle(title);
168  // prevents the user from escaping the dialog by hitting the Back button
169  alertDialog.setCancelable(false);
170  alertDialog.setMessage(stringToHTML(message));
171  alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
172  buttonText, new DialogInterface.OnClickListener() {
173  public void onClick(DialogInterface dialog, int which) {
174  if (callBack != null) {
175  callBack.run();
176  }
177  }});
178  alertDialog.show();
179  }
180 
181  // A version of oneButtonAlert that doesn't accept a callback. We provide this
182  // for backwards compatibility in case extensions out there are using this (older)
183  // version which didn't accept a callback
184  public static void oneButtonAlert(Activity activity,String message, String title, String buttonText) {
185  oneButtonAlert(activity, message, title, buttonText, null);
186  }
187 
188  // converts a string that includes HTML tags to a spannable string that can
189  // be included in an alert
190  private static SpannableString stringToHTML(String message) {
191  return new SpannableString(Html.fromHtml(message));
192  }
193 
208  @SimpleFunction(description = "Shows a dialog box with two buttons, from which the user can choose. "
209  + " If cancelable is true there will be an additional CANCEL button. "
210  + "Pressing a button will raise the AfterChoosing event. The \"choice\" parameter to AfterChoosing "
211  + "will be the text on the button that was pressed, or \"Cancel\" if the "
212  + " CANCEL button was pressed.")
213  public void ShowChooseDialog(String message, String title, final String button1Text,
214  final String button2Text, boolean cancelable) {
215  twoButtonDialog(activity,
216  message,
217  title,
218  button1Text,
219  button2Text,
220  cancelable,
221  new Runnable() {public void run() {AfterChoosing(button1Text);}},
222  new Runnable() {public void run() {AfterChoosing(button2Text);}},
223  new Runnable() {
224  public void run() {
226  AfterChoosing(activity.getString(android.R.string.cancel)); // backward compatible
227  }
228  }
229  );
230  }
231 
232  // This method takes three runnables that specify the actions to be performed
233  // when the buttons are pressed. It's declared static with an explicit activity input
234  // so that other components can use it.
235  public static void twoButtonDialog(Activity activity, String message, String title,
236  final String button1Text, final String button2Text, boolean cancelable,
237  final Runnable positiveAction, final Runnable negativeAction, final Runnable cancelAction) {
238  Log.i(LOG_TAG, "ShowChooseDialog: " + message);
239  AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
240  alertDialog.setTitle(title);
241  // prevents the user from escaping the dialog by hitting the Back button
242  alertDialog.setCancelable(false);
243  alertDialog.setMessage(stringToHTML(message));
244 
245  // Warning: The SDK button names are confusing. If there are
246  // three buttons, they go in the order POSITIVE | NEUTRAL | NEGATIVE
247  // In our notifier, we want choices like YES | NO | CANCEL, so NO maps to
248  // neutral and CANCEL maps to NEGATIVE.
249  alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, button1Text,
250  new DialogInterface.OnClickListener() {
251  public void onClick(DialogInterface dialog, int which) {
252  positiveAction.run();
253  }
254  });
255  alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, button2Text,
256  new DialogInterface.OnClickListener() {
257  public void onClick(DialogInterface dialog, int which) {
258  negativeAction.run();
259  }
260  });
261 
262  //If cancelable is true, then a 3rd button, with text of Cancel will be added
263  // and will raise AfterChoosing when pressed.
264  if (cancelable) {
265  // TODO(hal): It would be safer and more consistent to pass in cancelButtonText as a parameter.
266  final String cancelButtonText = activity.getString(android.R.string.cancel);
267  alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, cancelButtonText,
268  new DialogInterface.OnClickListener() {
269  public void onClick(DialogInterface dialog, int which) {
270  cancelAction.run();
271  }
272  });
273  }
274  alertDialog.show();
275  }
276 
282  @SimpleEvent(
283  description = "Event after the user has made a selection for ShowChooseDialog.")
284  public void AfterChoosing(String choice) {
285  EventDispatcher.dispatchEvent(this, "AfterChoosing", choice);
286  }
287 
292  @SimpleEvent(
293  description = "Event raised when the user canceled ShowChooseDialog.")
294  public void ChoosingCanceled() {
295  EventDispatcher.dispatchEvent(this, "ChoosingCanceled");
296  }
297 
312  @SimpleFunction(description = "Shows a dialog box where the user can enter text, after which the "
313  + "AfterTextInput event will be raised. If cancelable is true there will be an additional CANCEL button. "
314  + "Entering text will raise the AfterTextInput event. The \"response\" parameter to AfterTextInput "
315  + "will be the text that was entered, or \"Cancel\" if the CANCEL button was pressed.")
316 
317  public void ShowTextDialog(String message, String title, boolean cancelable) {
318  textInputDialog(message, title, cancelable, false);
319  }
320 
334  @SimpleFunction(description = "Shows a dialog box where the user can enter password (input is masked), "
335  + "after which the AfterTextInput event will be raised. If cancelable is true there will be an "
336  + "additional CANCEL button. Entering password will raise the AfterTextInput event. The \"response\" "
337  + "parameter to AfterTextInput will be the entered password, or \"Cancel\" if CANCEL button was pressed.")
338 
339  public void ShowPasswordDialog(String message, String title, boolean cancelable) {
340  textInputDialog(message, title, cancelable, true);
341  }
342 
356  // TODO(hal): It would be cleaner to define this in terms of oneButtonAlert and generalize
357  // oneButtonAlert so it can be used both for messages and text input. We could have merged
358  // this method into ShowTextDialog, but that would make it harder to do the generalization.
359  private void textInputDialog(String message, String title, boolean cancelable, boolean maskInput) {
360  final AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
361  alertDialog.setTitle(title);
362  alertDialog.setMessage(stringToHTML(message));
363  // Set an EditText view to get user input
364  final EditText input = new EditText(activity);
365  if (maskInput) {
366  input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
367  }
368  alertDialog.setView(input);
369  // prevents the user from escaping the dialog by hitting the Back button
370  alertDialog.setCancelable(false);
371  alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
372  new DialogInterface.OnClickListener() {
373  public void onClick(DialogInterface dialog, int which) {
374  HideKeyboard((View) input);
375  AfterTextInput(input.getText().toString());
376  }
377  });
378 
379  //If cancelable, then add the CANCEL button
380  if (cancelable) {
381  final String cancelButtonText = activity.getString(android.R.string.cancel);
382  alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, cancelButtonText,
383  new DialogInterface.OnClickListener() {
384  public void onClick(DialogInterface dialog, int which) {
385  HideKeyboard((View) input);
387  //User pressed CANCEL. Raise AfterTextInput with CANCEL
388  AfterTextInput(cancelButtonText);
389  }
390  });
391  }
392  alertDialog.show();
393  }
394 
398  public void HideKeyboard(View view) {
399  if (view != null) {
400  InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
401  imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
402  }
403  }
404 
409  @SimpleEvent(
410  description = "Event raised after the user has responded to ShowTextDialog.")
411  public void AfterTextInput(String response) {
412  EventDispatcher.dispatchEvent(this, "AfterTextInput", response);
413  }
414 
421  @SimpleEvent(
422  description = "Event raised when the user canceled ShowTextDialog.")
423  public void TextInputCanceled() {
424  EventDispatcher.dispatchEvent(this, "TextInputCanceled");
425  }
426 
427 
428 
435  public void ShowAlert(final String notice) {
436  handler.post(new Runnable() {
437  public void run() {
438  toastNow(notice);
439  }
440  });
441  }
442 
450  defaultValue = Component.TOAST_LENGTH_LONG + "")
452  userVisible = false)
453  public void NotifierLength(int length){
454  notifierLength = length;
455  }
456 
461  description="Specifies the length of time that the alert is shown -- either \"short\" or \"long\".",
462  category = PropertyCategory.APPEARANCE)
463  public int NotifierLength() {
464  return notifierLength;
465  }
466 
473  defaultValue = Component.DEFAULT_VALUE_COLOR_DKGRAY)
474  @SimpleProperty(description="Specifies the background color for alerts (not dialogs).")
475  public void BackgroundColor(@IsColor int argb) {
476  backgroundColor = argb;
477  }
478 
484  @SimpleProperty(description = "Specifies the text color for alerts (not dialogs).",
485  category = PropertyCategory.APPEARANCE)
486  @IsColor
487  public int TextColor() {
488  return textColor;
489  }
490 
497  defaultValue = Component.DEFAULT_VALUE_COLOR_WHITE)
499  public void TextColor(int argb) {
500  textColor = argb;
501  }
502 
503  // show a toast using a TextView, which allows us to set the
504  // font size. The default toast is too small.
505  private void toastNow (String message) {
506  // The notifier font size for more recent releases seems too
507  // small compared to early releases.
508  // This sets the fontsize according to SDK level, There is almost certainly
509  // a better way to do this, with display metrics for example, but
510  // I (Hal) can't figure it out.
511  int fontsize = (SdkLevel.getLevel() >= SdkLevel.LEVEL_ICE_CREAM_SANDWICH)
512  ? 22 : 15;
513  Toast toast = Toast.makeText(activity, message, notifierLength);
514  toast.setGravity(Gravity.CENTER, toast.getXOffset() / 2, toast.getYOffset() / 2);
515  TextView textView = new TextView(activity);
516  textView.setBackgroundColor(backgroundColor);
517  textView.setTextColor(textColor);
518  textView.setTextSize(fontsize);
519  Typeface typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
520  textView.setTypeface(typeface);
521  textView.setPadding(10, 10, 10, 10);
522  // Note: The space added to the message below is a patch to work around a bug where,
523  // in a message with multiple words, the trailing words do not appear. Whether this
524  // bug happens depends on the version of the OS and the exact characters in the message.
525  // The cause of the bug is that the textView parameter are not being set correctly -- I don't know
526  // why not. But as a result, Android will sometimes compute the length of the required
527  // textbox as one or two pixels short. Then, when the message is displayed, the
528  // wordwrap mechanism pushes the rest of the words to a "next line" that does not
529  // exist. Adding the space ensures that the width allocated for the text will be adequate.
530  textView.setText(message + " ");
531  toast.setView(textView);
532  toast.show();
533  }
534 
541  @SimpleFunction(description = "Writes an error message to the Android system log. " +
542  "See the Google Android documentation for how to access the log.")
543  public void LogError(String message) {
544  Log.e(LOG_TAG, message);
545  }
546 
553  @SimpleFunction(description = "Writes a warning message to the Android log. " +
554  "See the Google Android documentation for how to access the log.")
555  public void LogWarning(String message) {
556  Log.w(LOG_TAG, message);
557  }
558 
564  @SimpleFunction(description = "Writes an information message to the Android log.")
565  public void LogInfo(String message) {
566  Log.i(LOG_TAG, message);
567  }
568 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.Notifier.BackgroundColor
void BackgroundColor(@IsColor int argb)
Definition: Notifier.java:475
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.runtime.Notifier.ChoosingCanceled
void ChoosingCanceled()
Definition: Notifier.java:294
com.google.appinventor.components.runtime.Notifier.LogWarning
void LogWarning(String message)
Definition: Notifier.java:555
com.google.appinventor.components.runtime.Notifier.AfterTextInput
void AfterTextInput(String response)
Definition: Notifier.java:411
com.google.appinventor.components.runtime.Notifier.ShowChooseDialog
void ShowChooseDialog(String message, String title, final String button1Text, final String button2Text, boolean cancelable)
Definition: Notifier.java:213
com.google.appinventor.components
com.google.appinventor.components.runtime.Component.DEFAULT_VALUE_COLOR_WHITE
static final String DEFAULT_VALUE_COLOR_WHITE
Definition: Component.java:82
com.google.appinventor.components.runtime.Notifier.AfterChoosing
void AfterChoosing(String choice)
Definition: Notifier.java:284
com.google.appinventor.components.runtime.Component.DEFAULT_VALUE_COLOR_DKGRAY
static final String DEFAULT_VALUE_COLOR_DKGRAY
Definition: Component.java:74
com.google.appinventor.components.runtime.Notifier.HideKeyboard
void HideKeyboard(View view)
Definition: Notifier.java:398
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.runtime.Notifier.ShowProgressDialog
void ShowProgressDialog(String message, String title)
Definition: Notifier.java:120
com.google.appinventor.components.runtime.Notifier.progressDialog
void progressDialog(String message, String title)
Definition: Notifier.java:141
com.google.appinventor.components.runtime.Notifier.LogInfo
void LogInfo(String message)
Definition: Notifier.java:565
com.google.appinventor.components.runtime.Notifier.ShowAlert
void ShowAlert(final String notice)
Definition: Notifier.java:435
com.google.appinventor.components.runtime.Notifier.TextColor
void TextColor(int argb)
Definition: Notifier.java:499
com.google.appinventor.components.runtime.Notifier.TextInputCanceled
void TextInputCanceled()
Definition: Notifier.java:423
com.google.appinventor.components.runtime.Notifier
Definition: Notifier.java:78
com.google.appinventor.components.runtime.Notifier.TextColor
int TextColor()
Definition: Notifier.java:487
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.runtime.util.SdkLevel
Definition: SdkLevel.java:19
com.google.appinventor.components.runtime.Notifier.oneButtonAlert
static void oneButtonAlert(Activity activity, String message, String title, String buttonText)
Definition: Notifier.java:184
com.google.appinventor.components.runtime.util.SdkLevel.LEVEL_ICE_CREAM_SANDWICH
static final int LEVEL_ICE_CREAM_SANDWICH
Definition: SdkLevel.java:30
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.Notifier.ShowPasswordDialog
void ShowPasswordDialog(String message, String title, boolean cancelable)
Definition: Notifier.java:339
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.SdkLevel.getLevel
static int getLevel()
Definition: SdkLevel.java:45
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_TOAST_LENGTH
static final String PROPERTY_TYPE_TOAST_LENGTH
Definition: PropertyTypeConstants.java:266
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.Notifier.ShowTextDialog
void ShowTextDialog(String message, String title, boolean cancelable)
Definition: Notifier.java:317
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_COLOR
static final String PROPERTY_TYPE_COLOR
Definition: PropertyTypeConstants.java:63
com.google.appinventor.components.runtime.Notifier.Notifier
Notifier(ComponentContainer container)
Definition: Notifier.java:99
com.google
com
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.Notifier.LogError
void LogError(String message)
Definition: Notifier.java:543
com.google.appinventor.components.runtime.Notifier.oneButtonAlert
static void oneButtonAlert(Activity activity, String message, String title, String buttonText, final Runnable callBack)
Definition: Notifier.java:164
com.google.appinventor.components.runtime.Notifier.ShowMessageDialog
void ShowMessageDialog(String message, String title, String buttonText)
Definition: Notifier.java:158
com.google.appinventor.components.runtime.Component.TOAST_LENGTH_LONG
static final int TOAST_LENGTH_LONG
Definition: Component.java:129
com.google.appinventor.components.runtime.Notifier.DismissProgressDialog
void DismissProgressDialog()
Definition: Notifier.java:128
com.google.appinventor.components.annotations.IsColor
Definition: IsColor.java:13
com.google.appinventor.components.runtime.Notifier.twoButtonDialog
static void twoButtonDialog(Activity activity, String message, String title, final String button1Text, final String button2Text, boolean cancelable, final Runnable positiveAction, final Runnable negativeAction, final Runnable cancelAction)
Definition: Notifier.java:235
com.google.appinventor.components.annotations.PropertyCategory.APPEARANCE
APPEARANCE
Definition: PropertyCategory.java:16
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.annotations
com.google.appinventor
com.google.appinventor.components.runtime.Notifier.NotifierLength
int NotifierLength()
Definition: Notifier.java:463