michael@0: /* michael@0: Licensed to the Apache Software Foundation (ASF) under one michael@0: or more contributor license agreements. See the NOTICE file michael@0: distributed with this work for additional information michael@0: regarding copyright ownership. The ASF licenses this file michael@0: to you under the Apache License, Version 2.0 (the michael@0: "License"); you may not use this file except in compliance michael@0: with the License. You may obtain a copy of the License at michael@0: michael@0: http://www.apache.org/licenses/LICENSE-2.0 michael@0: michael@0: Unless required by applicable law or agreed to in writing, michael@0: software distributed under the License is distributed on an michael@0: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY michael@0: KIND, either express or implied. See the License for the michael@0: specific language governing permissions and limitations michael@0: under the License. michael@0: */ michael@0: package org.apache.cordova.dialogs; michael@0: michael@0: import org.apache.cordova.CallbackContext; michael@0: import org.apache.cordova.CordovaInterface; michael@0: import org.apache.cordova.CordovaPlugin; michael@0: import org.apache.cordova.PluginResult; michael@0: import org.json.JSONArray; michael@0: import org.json.JSONException; michael@0: import org.json.JSONObject; michael@0: michael@0: import android.annotation.SuppressLint; michael@0: import android.app.AlertDialog; michael@0: import android.app.AlertDialog.Builder; michael@0: import android.app.ProgressDialog; michael@0: import android.content.DialogInterface; michael@0: import android.media.Ringtone; michael@0: import android.media.RingtoneManager; michael@0: import android.net.Uri; michael@0: import android.widget.EditText; michael@0: import android.widget.TextView; michael@0: michael@0: michael@0: /** michael@0: * This class provides access to notifications on the device. michael@0: * michael@0: * Be aware that this implementation gets called on michael@0: * navigator.notification.{alert|confirm|prompt}, and that there is a separate michael@0: * implementation in org.apache.cordova.CordovaChromeClient that gets michael@0: * called on a simple window.{alert|confirm|prompt}. michael@0: */ michael@0: public class Notification extends CordovaPlugin { michael@0: michael@0: public int confirmResult = -1; michael@0: public ProgressDialog spinnerDialog = null; michael@0: public ProgressDialog progressDialog = null; michael@0: michael@0: /** michael@0: * Constructor. michael@0: */ michael@0: public Notification() { michael@0: } michael@0: michael@0: /** michael@0: * Executes the request and returns PluginResult. michael@0: * michael@0: * @param action The action to execute. michael@0: * @param args JSONArray of arguments for the plugin. michael@0: * @param callbackContext The callback context used when calling back into JavaScript. michael@0: * @return True when the action was valid, false otherwise. michael@0: */ michael@0: public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { michael@0: /* michael@0: * Don't run any of these if the current activity is finishing michael@0: * in order to avoid android.view.WindowManager$BadTokenException michael@0: * crashing the app. Just return true here since false should only michael@0: * be returned in the event of an invalid action. michael@0: */ michael@0: if(this.cordova.getActivity().isFinishing()) return true; michael@0: michael@0: if (action.equals("beep")) { michael@0: this.beep(args.getLong(0)); michael@0: } michael@0: else if (action.equals("alert")) { michael@0: this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext); michael@0: return true; michael@0: } michael@0: else if (action.equals("confirm")) { michael@0: this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext); michael@0: return true; michael@0: } michael@0: else if (action.equals("prompt")) { michael@0: this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), callbackContext); michael@0: return true; michael@0: } michael@0: else if (action.equals("activityStart")) { michael@0: this.activityStart(args.getString(0), args.getString(1)); michael@0: } michael@0: else if (action.equals("activityStop")) { michael@0: this.activityStop(); michael@0: } michael@0: else if (action.equals("progressStart")) { michael@0: this.progressStart(args.getString(0), args.getString(1)); michael@0: } michael@0: else if (action.equals("progressValue")) { michael@0: this.progressValue(args.getInt(0)); michael@0: } michael@0: else if (action.equals("progressStop")) { michael@0: this.progressStop(); michael@0: } michael@0: else { michael@0: return false; michael@0: } michael@0: michael@0: // Only alert and confirm are async. michael@0: callbackContext.success(); michael@0: return true; michael@0: } michael@0: michael@0: //-------------------------------------------------------------------------- michael@0: // LOCAL METHODS michael@0: //-------------------------------------------------------------------------- michael@0: michael@0: /** michael@0: * Beep plays the default notification ringtone. michael@0: * michael@0: * @param count Number of times to play notification michael@0: */ michael@0: public void beep(final long count) { michael@0: cordova.getThreadPool().execute(new Runnable() { michael@0: public void run() { michael@0: Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); michael@0: Ringtone notification = RingtoneManager.getRingtone(cordova.getActivity().getBaseContext(), ringtone); michael@0: michael@0: // If phone is not set to silent mode michael@0: if (notification != null) { michael@0: for (long i = 0; i < count; ++i) { michael@0: notification.play(); michael@0: long timeout = 5000; michael@0: while (notification.isPlaying() && (timeout > 0)) { michael@0: timeout = timeout - 100; michael@0: try { michael@0: Thread.sleep(100); michael@0: } catch (InterruptedException e) { michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Builds and shows a native Android alert with given Strings michael@0: * @param message The message the alert should display michael@0: * @param title The title of the alert michael@0: * @param buttonLabel The label of the button michael@0: * @param callbackContext The callback context michael@0: */ michael@0: public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) { michael@0: final CordovaInterface cordova = this.cordova; michael@0: michael@0: Runnable runnable = new Runnable() { michael@0: public void run() { michael@0: michael@0: AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: dlg.setMessage(message); michael@0: dlg.setTitle(title); michael@0: dlg.setCancelable(true); michael@0: dlg.setPositiveButton(buttonLabel, michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); michael@0: } michael@0: }); michael@0: dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { michael@0: public void onCancel(DialogInterface dialog) michael@0: { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); michael@0: } michael@0: }); michael@0: michael@0: changeTextDirection(dlg); michael@0: }; michael@0: }; michael@0: this.cordova.getActivity().runOnUiThread(runnable); michael@0: } michael@0: michael@0: /** michael@0: * Builds and shows a native Android confirm dialog with given title, message, buttons. michael@0: * This dialog only shows up to 3 buttons. Any labels after that will be ignored. michael@0: * The index of the button pressed will be returned to the JavaScript callback identified by callbackId. michael@0: * michael@0: * @param message The message the dialog should display michael@0: * @param title The title of the dialog michael@0: * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) michael@0: * @param callbackContext The callback context. michael@0: */ michael@0: public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) { michael@0: final CordovaInterface cordova = this.cordova; michael@0: michael@0: Runnable runnable = new Runnable() { michael@0: public void run() { michael@0: AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: dlg.setMessage(message); michael@0: dlg.setTitle(title); michael@0: dlg.setCancelable(true); michael@0: michael@0: // First button michael@0: if (buttonLabels.length() > 0) { michael@0: try { michael@0: dlg.setNegativeButton(buttonLabels.getString(0), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: michael@0: // Second button michael@0: if (buttonLabels.length() > 1) { michael@0: try { michael@0: dlg.setNeutralButton(buttonLabels.getString(1), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: michael@0: // Third button michael@0: if (buttonLabels.length() > 2) { michael@0: try { michael@0: dlg.setPositiveButton(buttonLabels.getString(2), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { michael@0: public void onCancel(DialogInterface dialog) michael@0: { michael@0: dialog.dismiss(); michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); michael@0: } michael@0: }); michael@0: michael@0: changeTextDirection(dlg); michael@0: }; michael@0: }; michael@0: this.cordova.getActivity().runOnUiThread(runnable); michael@0: } michael@0: michael@0: /** michael@0: * Builds and shows a native Android prompt dialog with given title, message, buttons. michael@0: * This dialog only shows up to 3 buttons. Any labels after that will be ignored. michael@0: * The following results are returned to the JavaScript callback identified by callbackId: michael@0: * buttonIndex Index number of the button selected michael@0: * input1 The text entered in the prompt dialog box michael@0: * michael@0: * @param message The message the dialog should display michael@0: * @param title The title of the dialog michael@0: * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) michael@0: * @param callbackContext The callback context. michael@0: */ michael@0: public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final String defaultText, final CallbackContext callbackContext) { michael@0: michael@0: final CordovaInterface cordova = this.cordova; michael@0: michael@0: Runnable runnable = new Runnable() { michael@0: public void run() { michael@0: final EditText promptInput = new EditText(cordova.getActivity()); michael@0: promptInput.setHint(defaultText); michael@0: AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: dlg.setMessage(message); michael@0: dlg.setTitle(title); michael@0: dlg.setCancelable(true); michael@0: michael@0: dlg.setView(promptInput); michael@0: michael@0: final JSONObject result = new JSONObject(); michael@0: michael@0: // First button michael@0: if (buttonLabels.length() > 0) { michael@0: try { michael@0: dlg.setNegativeButton(buttonLabels.getString(0), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: try { michael@0: result.put("buttonIndex",1); michael@0: result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); michael@0: } catch (JSONException e) { e.printStackTrace(); } michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: michael@0: // Second button michael@0: if (buttonLabels.length() > 1) { michael@0: try { michael@0: dlg.setNeutralButton(buttonLabels.getString(1), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: try { michael@0: result.put("buttonIndex",2); michael@0: result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); michael@0: } catch (JSONException e) { e.printStackTrace(); } michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: michael@0: // Third button michael@0: if (buttonLabels.length() > 2) { michael@0: try { michael@0: dlg.setPositiveButton(buttonLabels.getString(2), michael@0: new AlertDialog.OnClickListener() { michael@0: public void onClick(DialogInterface dialog, int which) { michael@0: dialog.dismiss(); michael@0: try { michael@0: result.put("buttonIndex",3); michael@0: result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); michael@0: } catch (JSONException e) { e.printStackTrace(); } michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); michael@0: } michael@0: }); michael@0: } catch (JSONException e) { } michael@0: } michael@0: dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { michael@0: public void onCancel(DialogInterface dialog){ michael@0: dialog.dismiss(); michael@0: try { michael@0: result.put("buttonIndex",0); michael@0: result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); michael@0: } catch (JSONException e) { e.printStackTrace(); } michael@0: callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); michael@0: } michael@0: }); michael@0: michael@0: changeTextDirection(dlg); michael@0: }; michael@0: }; michael@0: this.cordova.getActivity().runOnUiThread(runnable); michael@0: } michael@0: michael@0: /** michael@0: * Show the spinner. michael@0: * michael@0: * @param title Title of the dialog michael@0: * @param message The message of the dialog michael@0: */ michael@0: public synchronized void activityStart(final String title, final String message) { michael@0: if (this.spinnerDialog != null) { michael@0: this.spinnerDialog.dismiss(); michael@0: this.spinnerDialog = null; michael@0: } michael@0: final Notification notification = this; michael@0: final CordovaInterface cordova = this.cordova; michael@0: Runnable runnable = new Runnable() { michael@0: public void run() { michael@0: notification.spinnerDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: notification.spinnerDialog.setTitle(title); michael@0: notification.spinnerDialog.setMessage(message); michael@0: notification.spinnerDialog.setCancelable(true); michael@0: notification.spinnerDialog.setIndeterminate(true); michael@0: notification.spinnerDialog.setOnCancelListener( michael@0: new DialogInterface.OnCancelListener() { michael@0: public void onCancel(DialogInterface dialog) { michael@0: notification.spinnerDialog = null; michael@0: } michael@0: }); michael@0: notification.spinnerDialog.show(); michael@0: } michael@0: }; michael@0: this.cordova.getActivity().runOnUiThread(runnable); michael@0: } michael@0: michael@0: /** michael@0: * Stop spinner. michael@0: */ michael@0: public synchronized void activityStop() { michael@0: if (this.spinnerDialog != null) { michael@0: this.spinnerDialog.dismiss(); michael@0: this.spinnerDialog = null; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Show the progress dialog. michael@0: * michael@0: * @param title Title of the dialog michael@0: * @param message The message of the dialog michael@0: */ michael@0: public synchronized void progressStart(final String title, final String message) { michael@0: if (this.progressDialog != null) { michael@0: this.progressDialog.dismiss(); michael@0: this.progressDialog = null; michael@0: } michael@0: final Notification notification = this; michael@0: final CordovaInterface cordova = this.cordova; michael@0: Runnable runnable = new Runnable() { michael@0: public void run() { michael@0: notification.progressDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); michael@0: notification.progressDialog.setTitle(title); michael@0: notification.progressDialog.setMessage(message); michael@0: notification.progressDialog.setCancelable(true); michael@0: notification.progressDialog.setMax(100); michael@0: notification.progressDialog.setProgress(0); michael@0: notification.progressDialog.setOnCancelListener( michael@0: new DialogInterface.OnCancelListener() { michael@0: public void onCancel(DialogInterface dialog) { michael@0: notification.progressDialog = null; michael@0: } michael@0: }); michael@0: notification.progressDialog.show(); michael@0: } michael@0: }; michael@0: this.cordova.getActivity().runOnUiThread(runnable); michael@0: } michael@0: michael@0: /** michael@0: * Set value of progress bar. michael@0: * michael@0: * @param value 0-100 michael@0: */ michael@0: public synchronized void progressValue(int value) { michael@0: if (this.progressDialog != null) { michael@0: this.progressDialog.setProgress(value); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Stop progress dialog. michael@0: */ michael@0: public synchronized void progressStop() { michael@0: if (this.progressDialog != null) { michael@0: this.progressDialog.dismiss(); michael@0: this.progressDialog = null; michael@0: } michael@0: } michael@0: michael@0: @SuppressLint("NewApi") michael@0: private AlertDialog.Builder createDialog(CordovaInterface cordova) { michael@0: int currentapiVersion = android.os.Build.VERSION.SDK_INT; michael@0: if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { michael@0: return new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: } else { michael@0: return new AlertDialog.Builder(cordova.getActivity()); michael@0: } michael@0: } michael@0: michael@0: @SuppressLint("InlinedApi") michael@0: private ProgressDialog createProgressDialog(CordovaInterface cordova) { michael@0: int currentapiVersion = android.os.Build.VERSION.SDK_INT; michael@0: if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { michael@0: return new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); michael@0: } else { michael@0: return new ProgressDialog(cordova.getActivity()); michael@0: } michael@0: } michael@0: michael@0: @SuppressLint("NewApi") michael@0: private void changeTextDirection(Builder dlg){ michael@0: int currentapiVersion = android.os.Build.VERSION.SDK_INT; michael@0: dlg.create(); michael@0: AlertDialog dialog = dlg.show(); michael@0: if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { michael@0: TextView messageview = (TextView)dialog.findViewById(android.R.id.message); michael@0: messageview.setTextDirection(android.view.View.TEXT_DIRECTION_LOCALE); michael@0: } michael@0: } michael@0: }