Переглянути джерело

Revert "Minimize android-support-v4.jar to only include required classes"

This reverts commit 504b36cb09b6e1f9bccff5a6c5e14dbbecf61dfb.
Sebastián Katzer 11 роки тому
батько
коміт
b9ba9ab7e2
2 змінених файлів з 293 додано та 112 видалено
  1. BIN
      libs/android/android-support-v4.jar
  2. 293 112
      src/android/LocalNotification.java

BIN
libs/android/android-support-v4.jar


+ 293 - 112
src/android/LocalNotification.java

@@ -21,171 +21,352 @@
 
 package de.appplant.cordova.plugin.localnotification;
 
-import java.util.Calendar;
-import java.util.Random;
-
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 
-import android.annotation.SuppressLint;
-import android.support.v4.app.NotificationCompat;
-import android.support.v4.app.NotificationCompat.*;
+import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 
 /**
- * The alarm receiver is triggered when a scheduled alarm is fired. This class
- * reads the information in the intent and displays this information in the
- * Android notification bar. The notification uses the default notification
- * sound and it vibrates the phone.
+ * This plugin utilizes the Android AlarmManager in combination with StatusBar
+ * notifications. When a local notification is scheduled the alarm manager takes
+ * care of firing the event. When the event is processed, a notification is put
+ * in the Android status bar.
  */
-public class Receiver extends BroadcastReceiver {
+public class LocalNotification extends CordovaPlugin {
 
-    public static final String OPTIONS = "LOCAL_NOTIFICATION_OPTIONS";
+    protected final static String PLUGIN_NAME = "LocalNotification";
 
-    private Context context;
-    private Options options;
+    private   static CordovaWebView webView = null;
+    private   static Boolean deviceready = false;
+    protected static Context context = null;
+    protected static Boolean isInBackground = true;
+    private   static ArrayList<String> eventQueue = new ArrayList<String>();
 
     @Override
-    public void onReceive (Context context, Intent intent) {
-        Options options = null;
-        Bundle bundle   = intent.getExtras();
-        JSONObject args;
+    public void initialize (CordovaInterface cordova, CordovaWebView webView) {
+        super.initialize(cordova, webView);
 
-        try {
-            args    = new JSONObject(bundle.getString(OPTIONS));
-            options = new Options(context).parse(args);
-        } catch (JSONException e) {
-            return;
+        LocalNotification.webView = super.webView;
+        LocalNotification.context = super.cordova.getActivity().getApplicationContext();
+    }
+
+    @Override
+    public boolean execute (String action, final JSONArray args, CallbackContext callbackContext) throws JSONException {
+        if (action.equalsIgnoreCase("add")) {
+            cordova.getThreadPool().execute( new Runnable() {
+                public void run() {
+                    JSONObject arguments = args.optJSONObject(0);
+                    Options options      = new Options(context).parse(arguments);
+
+                    persist(options.getId(), args);
+                    add(options, true);
+                }
+            });
+
+            return true;
         }
 
-        this.context = context;
-        this.options = options;
+        if (action.equalsIgnoreCase("cancel")) {
+            cordova.getThreadPool().execute( new Runnable() {
+                public void run() {
+                    String id = args.optString(0);
 
-        // The context may got lost if the app was not running before
-        LocalNotification.setContext(context);
+                    cancel(id);
+                    unpersist(id);
+                }
+            });
 
-        fireTriggerEvent();
+            return true;
+        }
 
-        if (options.getInterval() == 0) {
-            LocalNotification.unpersist(options.getId());
-        } else if (isFirstAlarmInFuture()) {
-            return;
-        } else {
-            LocalNotification.add(options.moveDate(), false);
+        if (action.equalsIgnoreCase("cancelAll")) {
+            cordova.getThreadPool().execute( new Runnable() {
+                public void run() {
+                    cancelAll();
+                    unpersistAll();
+                }
+            });
+
+            return true;
+        }
+
+        if (action.equalsIgnoreCase("isScheduled")) {
+            String id = args.optString(0);
+
+            isScheduled(id, callbackContext);
+
+            return true;
+        }
+
+        if (action.equalsIgnoreCase("getScheduledIds")) {
+            getScheduledIds(callbackContext);
+
+            return true;
+        }
+
+        if (action.equalsIgnoreCase("deviceready")) {
+            cordova.getThreadPool().execute( new Runnable() {
+                public void run() {
+                    deviceready();
+                }
+            });
+
+            return true;
+        }
+
+        if (action.equalsIgnoreCase("pause")) {
+            isInBackground = true;
+
+            return true;
         }
 
-        Builder notification = buildNotification();
+        if (action.equalsIgnoreCase("resume")) {
+            isInBackground = false;
+
+            return true;
+        }
 
-        showNotification(notification);
+        // Returning false results in a "MethodNotFound" error.
+        return false;
     }
 
-    /*
-     * If you set a repeating alarm at 11:00 in the morning and it
-     * should trigger every morning at 08:00 o'clock, it will
-     * immediately fire. E.g. Android tries to make up for the
-     * 'forgotten' reminder for that day. Therefore we ignore the event
-     * if Android tries to 'catch up'.
+    /**
+     * Calls all pending callbacks after the deviceready event has been fired.
      */
-    private Boolean isFirstAlarmInFuture () {
-        if (options.getInterval() > 0) {
-            Calendar now    = Calendar.getInstance();
-            Calendar alarm  = options.getCalendar();
-
-            int alarmHour   = alarm.get(Calendar.HOUR_OF_DAY);
-            int alarmMin    = alarm.get(Calendar.MINUTE);
-            int currentHour = now.get(Calendar.HOUR_OF_DAY);
-            int currentMin  = now.get(Calendar.MINUTE);
-
-            if (currentHour != alarmHour && currentMin != alarmMin) {
-                return true;
-            }
+    private static void deviceready () {
+        deviceready = true;
+
+        for (String js : eventQueue) {
+            webView.sendJavascript(js);
         }
 
-        return false;
+        eventQueue.clear();
     }
 
     /**
-     * Creates the notification.
+     * Set an alarm.
+     *
+     * @param options
+     *            The options that can be specified per alarm.
+     * @param doFireEvent
+     *            If the onadd callback shall be called.
      */
-    @SuppressLint("NewApi")
-    private Builder buildNotification () {
-        Bitmap icon = BitmapFactory.decodeResource(context.getResources(), options.getIcon());
-        Uri sound   = options.getSound();
-
-        Builder notification = new NotificationCompat.Builder(context)
-            .setContentTitle(options.getTitle())
-            .setContentText(options.getMessage())
-            .setNumber(options.getBadge())
-            .setTicker(options.getMessage())
-            .setSmallIcon(options.getSmallIcon())
-            .setLargeIcon(icon)
-            .setAutoCancel(options.getAutoCancel())
-            .setOngoing(options.getOngoing());
-
-        if (sound != null) {
-            notification.setSound(sound);
+    public static void add (Options options, boolean doFireEvent) {
+        long triggerTime = options.getDate();
+
+        Intent intent = new Intent(context, Receiver.class)
+            .setAction("" + options.getId())
+            .putExtra(Receiver.OPTIONS, options.getJSONObject().toString());
+
+        AlarmManager am  = getAlarmManager();
+        PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+        if (doFireEvent) {
+            fireEvent("add", options.getId(), options.getJSON());
         }
 
-        if (Build.VERSION.SDK_INT > 16) {
-            notification.setStyle(new NotificationCompat.BigTextStyle()
-                .bigText(options.getMessage()));
+        am.set(AlarmManager.RTC_WAKEUP, triggerTime, pi);
+    }
+
+    /**
+     * Cancel a specific notification that was previously registered.
+     *
+     * @param notificationId
+     *            The original ID of the notification that was used when it was
+     *            registered using add()
+     */
+    public static void cancel (String notificationId) {
+        /*
+         * Create an intent that looks similar, to the one that was registered
+         * using add. Making sure the notification id in the action is the same.
+         * Now we can search for such an intent using the 'getService' method
+         * and cancel it.
+         */
+        Intent intent = new Intent(context, Receiver.class)
+            .setAction("" + notificationId);
+
+        PendingIntent pi       = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        AlarmManager am        = getAlarmManager();
+        NotificationManager nc = getNotificationManager();
+
+        am.cancel(pi);
+
+        try {
+            nc.cancel(Integer.parseInt(notificationId));
+        } catch (Exception e) {}
+
+        fireEvent("cancel", notificationId, "");
+    }
+
+    /**
+     * Cancel all notifications that were created by this plugin.
+     *
+     * Android can only unregister a specific alarm. There is no such thing
+     * as cancelAll. Therefore we rely on the Shared Preferences which holds
+     * all our alarms to loop through these alarms and unregister them one
+     * by one.
+     */
+    public static void cancelAll() {
+        SharedPreferences settings = getSharedPreferences();
+        NotificationManager nc     = getNotificationManager();
+        Map<String, ?> alarms      = settings.getAll();
+        Set<String> alarmIds       = alarms.keySet();
+
+        for (String alarmId : alarmIds) {
+            cancel(alarmId);
         }
 
-        setClickEvent(notification);
+        nc.cancelAll();
+    }
+
+    /**
+     * Checks wether a notification with an ID is scheduled.
+     *
+     * @param id
+     *          The notification ID to be check.
+     * @param callbackContext
+     */
+    public static void isScheduled (String id, CallbackContext callbackContext) {
+        SharedPreferences settings = getSharedPreferences();
+        Map<String, ?> alarms      = settings.getAll();
+        boolean isScheduled        = alarms.containsKey(id);
+        PluginResult result        = new PluginResult(PluginResult.Status.OK, isScheduled);
+
+        callbackContext.sendPluginResult(result);
+    }
+
+    /**
+     * Retrieves a list with all currently pending notifications.
+     *
+     * @param callbackContext
+     */
+    public static void getScheduledIds (CallbackContext callbackContext) {
+        SharedPreferences settings = getSharedPreferences();
+        Map<String, ?> alarms      = settings.getAll();
+        Set<String> alarmIds       = alarms.keySet();
+        JSONArray pendingIds       = new JSONArray(alarmIds);
 
-        return notification;
+        callbackContext.success(pendingIds);
     }
 
     /**
-     * Adds an onclick handler to the notification
+     * Persist the information of this alarm to the Android Shared Preferences.
+     * This will allow the application to restore the alarm upon device reboot.
+     * Also this is used by the cancelAll method.
+     *
+     * @param alarmId
+     *            The Id of the notification that must be persisted.
+     * @param args
+     *            The assumption is that parse has been called already.
      */
-    private Builder setClickEvent (Builder notification) {
-        Intent intent = new Intent(context, ReceiverActivity.class)
-            .putExtra(OPTIONS, options.getJSONObject().toString())
-            .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+    public static void persist (String alarmId, JSONArray args) {
+        Editor editor = getSharedPreferences().edit();
 
-        int requestCode = new Random().nextInt();
+        if (alarmId != null) {
+            editor.putString(alarmId, args.toString());
+            editor.apply();
+        }
+    }
 
-        PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    /**
+     * Remove a specific alarm from the Android shared Preferences.
+     *
+     * @param alarmId
+     *            The Id of the notification that must be removed.
+     */
+    public static void unpersist (String alarmId) {
+        Editor editor = getSharedPreferences().edit();
 
-        return notification.setContentIntent(contentIntent);
+        if (alarmId != null) {
+            editor.remove(alarmId);
+            editor.apply();
+        }
     }
 
     /**
-     * Shows the notification
+     * Clear all alarms from the Android shared Preferences.
      */
-    @SuppressWarnings("deprecation")
-    @SuppressLint("NewApi")
-    private void showNotification (Builder notification) {
-        NotificationManager mgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        int id                  = 0;
+    public static void unpersistAll () {
+        Editor editor = getSharedPreferences().edit();
 
-        try {
-            id = Integer.parseInt(options.getId());
-        } catch (Exception e) {}
+        editor.clear();
+        editor.apply();
+    }
 
-        if (Build.VERSION.SDK_INT<16) {
-            // build notification for HoneyComb to ICS
-            mgr.notify(id, notification.getNotification());
-        } else if (Build.VERSION.SDK_INT>15) {
-            // Notification for Jellybean and above
-            mgr.notify(id, notification.build());
+    /**
+     * Fires the given event.
+     *
+     * @param {String} event The Name of the event
+     * @param {String} id    The ID of the notification
+     * @param {String} json  A custom (JSON) string
+     */
+    public static void fireEvent (String event, String id, String json) {
+        String state  = getApplicationState();
+        String params = "\"" + id + "\",\"" + state + "\",\\'" + JSONObject.quote(json) + "\\'.replace(/(^\"|\"$)/g, \\'\\')";
+        String js     = "setTimeout('plugin.notification.local.on" + event + "(" + params + ")',0)";
+
+        // webview may available, but callbacks needs to be executed
+        // after deviceready
+        if (deviceready == false) {
+            eventQueue.add(js);
+        } else {
+            webView.sendJavascript(js);
         }
     }
 
     /**
-     * Fires ontrigger event.
+     * Retrieves the application state
+     *
+     * @return {String}
+     *      Either "background" or "foreground"
+     */
+    protected static String getApplicationState () {
+        return isInBackground ? "background" : "foreground";
+    }
+
+    /**
+     * Set the application context if not already set.
+     */
+    protected static void setContext (Context context) {
+        if (LocalNotification.context == null) {
+            LocalNotification.context = context;
+        }
+    }
+
+    /**
+     * The Local storage for the application.
+     */
+    protected static SharedPreferences getSharedPreferences () {
+        return context.getSharedPreferences(PLUGIN_NAME, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * The alarm manager for the application.
+     */
+    protected static AlarmManager getAlarmManager () {
+        return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+    }
+
+    /**
+     * The notification manager for the application.
      */
-    private void fireTriggerEvent () {
-        LocalNotification.fireEvent("trigger", options.getId(), options.getJSON());
+    protected static NotificationManager getNotificationManager () {
+        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
     }
-}
+}