UNPKG

23.6 kBtext/x-java-sourceView Raw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18*/
19package org.apache.cordova.dialogs;
20
21import android.annotation.SuppressLint;
22import android.app.AlertDialog;
23import android.app.AlertDialog.Builder;
24import android.app.ProgressDialog;
25import android.content.DialogInterface;
26import android.content.res.Resources;
27import android.media.Ringtone;
28import android.media.RingtoneManager;
29import android.net.Uri;
30import android.widget.EditText;
31import android.widget.TextView;
32
33import org.apache.cordova.CallbackContext;
34import org.apache.cordova.CordovaInterface;
35import org.apache.cordova.CordovaPlugin;
36import org.apache.cordova.LOG;
37import org.apache.cordova.PluginResult;
38import org.json.JSONArray;
39import org.json.JSONException;
40import org.json.JSONObject;
41
42
43/**
44 * This class provides access to notifications on the device.
45 *
46 * Be aware that this implementation gets called on
47 * navigator.notification.{alert|confirm|prompt}, and that there is a separate
48 * implementation in org.apache.cordova.CordovaChromeClient that gets
49 * called on a simple window.{alert|confirm|prompt}.
50 */
51public class Notification extends CordovaPlugin {
52
53 private static final String LOG_TAG = "Notification";
54
55 private static final String ACTION_BEEP = "beep";
56 private static final String ACTION_ALERT = "alert";
57 private static final String ACTION_CONFIRM = "confirm";
58 private static final String ACTION_PROMPT = "prompt";
59 private static final String ACTION_ACTIVITY_START = "activityStart";
60 private static final String ACTION_ACTIVITY_STOP = "activityStop";
61 private static final String ACTION_PROGRESS_START = "progressStart";
62 private static final String ACTION_PROGRESS_VALUE = "progressValue";
63 private static final String ACTION_PROGRESS_STOP = "progressStop";
64
65 private static final long BEEP_TIMEOUT = 5000;
66 private static final long BEEP_WAIT_TINE = 100;
67
68 public int confirmResult = -1;
69 public ProgressDialog spinnerDialog = null;
70 public ProgressDialog progressDialog = null;
71
72 /**
73 * Constructor.
74 */
75 public Notification() {
76 }
77
78 /**
79 * Executes the request and returns PluginResult.
80 *
81 * @param action The action to execute.
82 * @param args JSONArray of arguments for the plugin.
83 * @param callbackContext The callback context used when calling back into JavaScript.
84 * @return True when the action was valid, false otherwise.
85 */
86 public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
87 /*
88 * Don't run any of these if the current activity is finishing
89 * in order to avoid android.view.WindowManager$BadTokenException
90 * crashing the app. Just return true here since false should only
91 * be returned in the event of an invalid action.
92 */
93 if (this.cordova.getActivity().isFinishing()) return true;
94
95 if (action.equals(ACTION_BEEP)) {
96 this.beep(args.getLong(0));
97 }
98 else if (action.equals(ACTION_ALERT)) {
99 this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext);
100 return true;
101 }
102 else if (action.equals(ACTION_CONFIRM)) {
103 this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext);
104 return true;
105 }
106 else if (action.equals(ACTION_PROMPT)) {
107 this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), callbackContext);
108 return true;
109 }
110 else if (action.equals(ACTION_ACTIVITY_START)) {
111 this.activityStart(args.getString(0), args.getString(1));
112 }
113 else if (action.equals(ACTION_ACTIVITY_STOP)) {
114 this.activityStop();
115 }
116 else if (action.equals(ACTION_PROGRESS_START)) {
117 this.progressStart(args.getString(0), args.getString(1));
118 }
119 else if (action.equals(ACTION_PROGRESS_VALUE)) {
120 this.progressValue(args.getInt(0));
121 }
122 else if (action.equals(ACTION_PROGRESS_STOP)) {
123 this.progressStop();
124 }
125 else {
126 return false;
127 }
128
129 // Only alert and confirm are async.
130 callbackContext.success();
131 return true;
132 }
133
134 //--------------------------------------------------------------------------
135 // LOCAL METHODS
136 //--------------------------------------------------------------------------
137
138 /**
139 * Beep plays the default notification ringtone.
140 *
141 * @param count Number of times to play notification
142 */
143 public void beep(final long count) {
144 cordova.getThreadPool().execute(new Runnable() {
145 public void run() {
146 Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
147 Ringtone notification = RingtoneManager.getRingtone(cordova.getActivity().getBaseContext(), ringtone);
148
149 // If phone is not set to silent mode
150 if (notification != null) {
151 for (long i = 0; i < count; ++i) {
152 notification.play();
153 long timeout = BEEP_TIMEOUT;
154 while (notification.isPlaying() && (timeout > 0)) {
155 timeout = timeout - BEEP_WAIT_TINE;
156 try {
157 Thread.sleep(BEEP_WAIT_TINE);
158 } catch (InterruptedException e) {
159 Thread.currentThread().interrupt();
160 }
161 }
162 }
163 }
164 }
165 });
166 }
167
168 /**
169 * Builds and shows a native Android alert with given Strings
170 * @param message The message the alert should display
171 * @param title The title of the alert
172 * @param buttonLabel The label of the button
173 * @param callbackContext The callback context
174 */
175 public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) {
176 final CordovaInterface cordova = this.cordova;
177
178 Runnable runnable = new Runnable() {
179 public void run() {
180
181 Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
182 dlg.setMessage(message);
183 dlg.setTitle(title);
184 dlg.setCancelable(true);
185 dlg.setPositiveButton(buttonLabel,
186 new AlertDialog.OnClickListener() {
187 public void onClick(DialogInterface dialog, int which) {
188 dialog.dismiss();
189 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
190 }
191 });
192 dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
193 public void onCancel(DialogInterface dialog)
194 {
195 dialog.dismiss();
196 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
197 }
198 });
199
200 changeTextDirection(dlg);
201 };
202 };
203 this.cordova.getActivity().runOnUiThread(runnable);
204 }
205
206 /**
207 * Builds and shows a native Android confirm dialog with given title, message, buttons.
208 * This dialog only shows up to 3 buttons. Any labels after that will be ignored.
209 * The index of the button pressed will be returned to the JavaScript callback identified by callbackId.
210 *
211 * @param message The message the dialog should display
212 * @param title The title of the dialog
213 * @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
214 * @param callbackContext The callback context.
215 */
216 public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) {
217 final CordovaInterface cordova = this.cordova;
218
219 Runnable runnable = new Runnable() {
220 public void run() {
221 Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
222 dlg.setMessage(message);
223 dlg.setTitle(title);
224 dlg.setCancelable(true);
225
226 // First button
227 if (buttonLabels.length() > 0) {
228 try {
229 dlg.setNegativeButton(buttonLabels.getString(0),
230 new AlertDialog.OnClickListener() {
231 public void onClick(DialogInterface dialog, int which) {
232 dialog.dismiss();
233 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1));
234 }
235 });
236 } catch (JSONException e) {
237 LOG.d(LOG_TAG,"JSONException on first button.");
238 }
239 }
240
241 // Second button
242 if (buttonLabels.length() > 1) {
243 try {
244 dlg.setNeutralButton(buttonLabels.getString(1),
245 new AlertDialog.OnClickListener() {
246 public void onClick(DialogInterface dialog, int which) {
247 dialog.dismiss();
248 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2));
249 }
250 });
251 } catch (JSONException e) {
252 LOG.d(LOG_TAG,"JSONException on second button.");
253 }
254 }
255
256 // Third button
257 if (buttonLabels.length() > 2) {
258 try {
259 dlg.setPositiveButton(buttonLabels.getString(2),
260 new AlertDialog.OnClickListener() {
261 public void onClick(DialogInterface dialog, int which) {
262 dialog.dismiss();
263 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3));
264 }
265 });
266 } catch (JSONException e) {
267 LOG.d(LOG_TAG,"JSONException on third button.");
268 }
269 }
270 dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
271 public void onCancel(DialogInterface dialog)
272 {
273 dialog.dismiss();
274 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
275 }
276 });
277
278 changeTextDirection(dlg);
279 };
280 };
281 this.cordova.getActivity().runOnUiThread(runnable);
282 }
283
284 /**
285 * Builds and shows a native Android prompt dialog with given title, message, buttons.
286 * This dialog only shows up to 3 buttons. Any labels after that will be ignored.
287 * The following results are returned to the JavaScript callback identified by callbackId:
288 * buttonIndex Index number of the button selected
289 * input1 The text entered in the prompt dialog box
290 *
291 * @param message The message the dialog should display
292 * @param title The title of the dialog
293 * @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
294 * @param callbackContext The callback context.
295 */
296 public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final String defaultText, final CallbackContext callbackContext) {
297
298 final CordovaInterface cordova = this.cordova;
299
300 Runnable runnable = new Runnable() {
301 public void run() {
302 final EditText promptInput = new EditText(cordova.getActivity());
303
304 /* CB-11677 - By default, prompt input text color is set according current theme.
305 But for some android versions is not visible (for example 5.1.1).
306 android.R.color.primary_text_light will make text visible on all versions. */
307 Resources resources = cordova.getActivity().getResources();
308 int promptInputTextColor = resources.getColor(android.R.color.primary_text_light);
309 promptInput.setTextColor(promptInputTextColor);
310 promptInput.setText(defaultText);
311 Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
312 dlg.setMessage(message);
313 dlg.setTitle(title);
314 dlg.setCancelable(true);
315
316 dlg.setView(promptInput);
317
318 final JSONObject result = new JSONObject();
319
320 // First button
321 if (buttonLabels.length() > 0) {
322 try {
323 dlg.setNegativeButton(buttonLabels.getString(0),
324 new AlertDialog.OnClickListener() {
325 public void onClick(DialogInterface dialog, int which) {
326 dialog.dismiss();
327 try {
328 result.put("buttonIndex",1);
329 result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
330 } catch (JSONException e) {
331 LOG.d(LOG_TAG,"JSONException on first button.", e);
332 }
333 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
334 }
335 });
336 } catch (JSONException e) {
337 LOG.d(LOG_TAG,"JSONException on first button.");
338 }
339 }
340
341 // Second button
342 if (buttonLabels.length() > 1) {
343 try {
344 dlg.setNeutralButton(buttonLabels.getString(1),
345 new AlertDialog.OnClickListener() {
346 public void onClick(DialogInterface dialog, int which) {
347 dialog.dismiss();
348 try {
349 result.put("buttonIndex",2);
350 result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
351 } catch (JSONException e) {
352 LOG.d(LOG_TAG,"JSONException on second button.", e);
353 }
354 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
355 }
356 });
357 } catch (JSONException e) {
358 LOG.d(LOG_TAG,"JSONException on second button.");
359 }
360 }
361
362 // Third button
363 if (buttonLabels.length() > 2) {
364 try {
365 dlg.setPositiveButton(buttonLabels.getString(2),
366 new AlertDialog.OnClickListener() {
367 public void onClick(DialogInterface dialog, int which) {
368 dialog.dismiss();
369 try {
370 result.put("buttonIndex",3);
371 result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
372 } catch (JSONException e) {
373 LOG.d(LOG_TAG,"JSONException on third button.", e);
374 }
375 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
376 }
377 });
378 } catch (JSONException e) {
379 LOG.d(LOG_TAG,"JSONException on third button.");
380 }
381 }
382 dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
383 public void onCancel(DialogInterface dialog){
384 dialog.dismiss();
385 try {
386 result.put("buttonIndex",0);
387 result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
388 } catch (JSONException e) { e.printStackTrace(); }
389 callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
390 }
391 });
392
393 changeTextDirection(dlg);
394 };
395 };
396 this.cordova.getActivity().runOnUiThread(runnable);
397 }
398
399 /**
400 * Show the spinner.
401 *
402 * @param title Title of the dialog
403 * @param message The message of the dialog
404 */
405 public synchronized void activityStart(final String title, final String message) {
406 if (this.spinnerDialog != null) {
407 this.spinnerDialog.dismiss();
408 this.spinnerDialog = null;
409 }
410 final Notification notification = this;
411 final CordovaInterface cordova = this.cordova;
412 Runnable runnable = new Runnable() {
413 public void run() {
414 notification.spinnerDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
415 notification.spinnerDialog.setTitle(title);
416 notification.spinnerDialog.setMessage(message);
417 notification.spinnerDialog.setCancelable(true);
418 notification.spinnerDialog.setIndeterminate(true);
419 notification.spinnerDialog.setOnCancelListener(
420 new DialogInterface.OnCancelListener() {
421 public void onCancel(DialogInterface dialog) {
422 notification.spinnerDialog = null;
423 }
424 });
425 notification.spinnerDialog.show();
426 }
427 };
428 this.cordova.getActivity().runOnUiThread(runnable);
429 }
430
431 /**
432 * Stop spinner.
433 */
434 public synchronized void activityStop() {
435 if (this.spinnerDialog != null) {
436 this.spinnerDialog.dismiss();
437 this.spinnerDialog = null;
438 }
439 }
440
441 /**
442 * Show the progress dialog.
443 *
444 * @param title Title of the dialog
445 * @param message The message of the dialog
446 */
447 public synchronized void progressStart(final String title, final String message) {
448 if (this.progressDialog != null) {
449 this.progressDialog.dismiss();
450 this.progressDialog = null;
451 }
452 final Notification notification = this;
453 final CordovaInterface cordova = this.cordova;
454 Runnable runnable = new Runnable() {
455 public void run() {
456 notification.progressDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
457 notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
458 notification.progressDialog.setTitle(title);
459 notification.progressDialog.setMessage(message);
460 notification.progressDialog.setCancelable(true);
461 notification.progressDialog.setMax(100);
462 notification.progressDialog.setProgress(0);
463 notification.progressDialog.setOnCancelListener(
464 new DialogInterface.OnCancelListener() {
465 public void onCancel(DialogInterface dialog) {
466 notification.progressDialog = null;
467 }
468 });
469 notification.progressDialog.show();
470 }
471 };
472 this.cordova.getActivity().runOnUiThread(runnable);
473 }
474
475 /**
476 * Set value of progress bar.
477 *
478 * @param value 0-100
479 */
480 public synchronized void progressValue(int value) {
481 if (this.progressDialog != null) {
482 this.progressDialog.setProgress(value);
483 }
484 }
485
486 /**
487 * Stop progress dialog.
488 */
489 public synchronized void progressStop() {
490 if (this.progressDialog != null) {
491 this.progressDialog.dismiss();
492 this.progressDialog = null;
493 }
494 }
495
496 @SuppressLint("NewApi")
497 private Builder createDialog(CordovaInterface cordova) {
498 int currentapiVersion = android.os.Build.VERSION.SDK_INT;
499 if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {
500 return new Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
501 } else {
502 return new Builder(cordova.getActivity());
503 }
504 }
505
506 @SuppressLint("InlinedApi")
507 private ProgressDialog createProgressDialog(CordovaInterface cordova) {
508 int currentapiVersion = android.os.Build.VERSION.SDK_INT;
509 if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
510 return new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
511 } else {
512 return new ProgressDialog(cordova.getActivity());
513 }
514 }
515
516 @SuppressLint("NewApi")
517 private void changeTextDirection(Builder dlg){
518 int currentapiVersion = android.os.Build.VERSION.SDK_INT;
519 dlg.create();
520 AlertDialog dialog = dlg.show();
521 if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
522 TextView messageview = (TextView)dialog.findViewById(android.R.id.message);
523 messageview.setTextDirection(android.view.View.TEXT_DIRECTION_LOCALE);
524 }
525 }
526}