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