Sebastián Katzer 8 anos atrás
pai
commit
cd78f87b4b

+ 15 - 3
src/android/ClearReceiver.java

@@ -21,9 +21,13 @@
 
 package de.appplant.cordova.plugin.localnotification;
 
+import android.os.Bundle;
+
 import de.appplant.cordova.plugin.notification.Notification;
 import de.appplant.cordova.plugin.notification.receiver.AbstractClearReceiver;
 
+import static de.appplant.cordova.plugin.notification.Request.EXTRA_LAST;
+
 
 /**
  * The clear intent receiver is triggered when the user clears a
@@ -35,11 +39,19 @@ public class ClearReceiver extends AbstractClearReceiver {
     /**
      * Called when a local notification was cleared from outside of the app.
      *
-     * @param notification Wrapper around the local notification
+     * @param notification Wrapper around the local notification.
+     * @param bundle       The bundled extras.
      */
     @Override
-    public void onClear (Notification notification) {
-        notification.clear();
+    public void onClear (Notification notification, Bundle bundle) {
+        boolean isLast = bundle.getBoolean(EXTRA_LAST, false);
+
+        if (isLast) {
+            notification.cancel();
+        } else {
+            notification.clear();
+        }
+
         LocalNotification.fireEvent("clear", notification);
     }
 

+ 49 - 22
src/android/ClickActivity.java

@@ -21,7 +21,6 @@
 
 package de.appplant.cordova.plugin.localnotification;
 
-import android.content.Intent;
 import android.os.Bundle;
 import android.support.v4.app.RemoteInput;
 
@@ -29,9 +28,11 @@ import org.json.JSONException;
 import org.json.JSONObject;
 
 import de.appplant.cordova.plugin.notification.Notification;
-import de.appplant.cordova.plugin.notification.Options;
 import de.appplant.cordova.plugin.notification.activity.AbstractClickActivity;
 
+import static de.appplant.cordova.plugin.notification.Options.EXTRA_LAUNCH;
+import static de.appplant.cordova.plugin.notification.Request.EXTRA_LAST;
+
 /**
  * The receiver activity is triggered when a notification is clicked by a user.
  * The activity calls the background callback and brings the launch intent
@@ -42,39 +43,65 @@ public class ClickActivity extends AbstractClickActivity {
     /**
      * Called when local notification was clicked by the user.
      *
-     * @param action       The name of the action.
      * @param notification Wrapper around the local notification.
+     * @param bundle       The bundled extras.
      */
     @Override
-    public void onClick(String action, Notification notification) {
+    public void onClick(Notification notification, Bundle bundle) {
+        String action    = getAction();
         JSONObject data  = new JSONObject();
-        Intent intent    = getIntent();
-        Bundle bundle    = intent.getExtras();
-        Bundle input     = RemoteInput.getResultsFromIntent(intent);
-        boolean doLaunch = bundle.getBoolean(Options.EXTRA_LAUNCH, true);
-
-        if (input != null) {
-            try {
-                data.put("text", input.getString(action, ""));
-            } catch (JSONException e) {
-                e.printStackTrace();
-            }
-        }
 
-        if (doLaunch) {
-            launchApp();
-        }
+        setTextInput(action, data);
+        launchAppIf();
 
         LocalNotification.fireEvent(action, notification, data);
 
         if (notification.getOptions().isSticky())
             return;
 
-        if (notification.isRepeating()) {
-            notification.clear();
-        } else {
+        if (isLast()) {
             notification.cancel();
+        } else {
+            notification.clear();
         }
     }
 
+    /**
+     * Set the text if any remote input is given.
+     *
+     * @param action The action where to look for.
+     * @param data   The object to extend.
+     */
+    private void setTextInput(String action, JSONObject data) {
+        Bundle input = RemoteInput.getResultsFromIntent(getIntent());
+
+        if (input == null)
+            return;
+
+        try {
+            data.put("text", input.getString(action));
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Launch app if requested by user.
+     */
+    private void launchAppIf() {
+        boolean doLaunch = getIntent().getBooleanExtra(EXTRA_LAUNCH, true);
+
+        if (!doLaunch)
+            return;
+
+        launchApp();
+    }
+
+    /**
+     * If the notification was the last scheduled one by request.
+     */
+    private boolean isLast() {
+        return getIntent().getBooleanExtra(EXTRA_LAST, false);
+    }
+
 }

+ 10 - 9
src/android/TriggerReceiver.java

@@ -21,6 +21,8 @@
 
 package de.appplant.cordova.plugin.localnotification;
 
+import android.os.Bundle;
+
 import de.appplant.cordova.plugin.notification.Builder;
 import de.appplant.cordova.plugin.notification.Notification;
 import de.appplant.cordova.plugin.notification.receiver.AbstractTriggerReceiver;
@@ -37,28 +39,27 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
      * Called when a local notification was triggered. Does present the local
      * notification, re-schedule the alarm if necessary and fire trigger event.
      *
-     * @param notification Wrapper around the local notification
-     * @param updated      If an update has triggered or the original
+     * @param notification Wrapper around the local notification.
+     * @param bundle       The bundled extras.
      */
     @Override
-    public void onTrigger (Notification notification, boolean updated) {
+    public void onTrigger (Notification notification, Bundle bundle) {
         notification.show();
-
-        if (!updated) {
-            LocalNotification.fireEvent("trigger", notification);
-        }
+        LocalNotification.fireEvent("trigger", notification);
     }
 
     /**
      * Build notification specified by options.
      *
-     * @param builder Notification builder
+     * @param builder Notification builder.
+     * @param bundle  The bundled extras.
      */
     @Override
-    public Notification buildNotification (Builder builder) {
+    public Notification buildNotification (Builder builder, Bundle bundle) {
         return builder
                 .setClickActivity(ClickActivity.class)
                 .setClearReceiver(ClearReceiver.class)
+                .setExtras(bundle)
                 .build();
     }
 

+ 24 - 8
src/android/notification/Builder.java

@@ -53,6 +53,9 @@ public final class Builder {
     // Activity to handle the click event
     private Class<?> clickActivity;
 
+    // Additional extras to merge into each intent
+    private Bundle extras;
+
     /**
      * Constructor
      *
@@ -83,6 +86,16 @@ public final class Builder {
         return this;
     }
 
+    /**
+     * Set bundle extras.
+     *
+     * @param extras The bundled extras to merge into.
+     */
+    public Builder setExtras(Bundle extras) {
+        this.extras = extras;
+        return this;
+    }
+
     /**
      * Creates the notification with all its options passed through JS.
      *
@@ -99,7 +112,7 @@ public final class Builder {
         Uri sound     = options.getSound();
         Bundle extras = new Bundle();
 
-        extras.putString(Options.EXTRA, options.toString());
+        extras.putInt(Notification.EXTRA_ID, options.getId());
         extras.putString(Options.EXTRA_SOUND, sound.toString());
 
         builder = new NotificationCompat.Builder(context, Manager.CHANNEL_ID)
@@ -266,8 +279,9 @@ public final class Builder {
             return;
 
         Intent intent = new Intent(context, clearReceiver)
+                .putExtras(extras)
                 .setAction(options.getIdentifier())
-                .putExtra(Options.EXTRA, options.toString());
+                .putExtra(Notification.EXTRA_ID, options.getId());
 
         PendingIntent deleteIntent = PendingIntent.getBroadcast(
                 context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -287,9 +301,10 @@ public final class Builder {
             return;
 
         Intent intent = new Intent(context, clickActivity)
-                .putExtra(Options.EXTRA, options.toString())
-                .putExtra(Options.EXTRA_LAUNCH, options.isLaunchingApp())
+                .putExtras(extras)
+                .putExtra(Notification.EXTRA_ID, options.getId())
                 .putExtra(Action.EXTRA_ID, Action.CLICK_ACTION_ID)
+                .putExtra(Options.EXTRA_LAUNCH, options.isLaunchingApp())
                 .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
 
         int reqCode = new Random().nextInt();
@@ -333,15 +348,16 @@ public final class Builder {
      */
     private PendingIntent getPendingIntentForAction (Action action) {
         Intent intent = new Intent(context, clickActivity)
-                .putExtra(Options.EXTRA, options.toString())
-                .putExtra(Options.EXTRA_LAUNCH, action.isLaunchingApp())
+                .putExtras(extras)
+                .putExtra(Notification.EXTRA_ID, options.getId())
                 .putExtra(Action.EXTRA_ID, action.getId())
+                .putExtra(Options.EXTRA_LAUNCH, action.isLaunchingApp())
                 .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
 
-        int requestCode = new Random().nextInt();
+        int reqCode = new Random().nextInt();
 
         return PendingIntent.getActivity(
-                context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+                context, reqCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
     }
 
 }

+ 46 - 103
src/android/notification/Manager.java

@@ -21,27 +21,19 @@
 
 package de.appplant.cordova.plugin.notification;
 
-import android.app.AlarmManager;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
+import android.content.SharedPreferences;
 import android.support.v4.app.NotificationManagerCompat;
 
-import java.util.Date;
+import org.json.JSONException;
+import org.json.JSONObject;
 
-import static android.app.AlarmManager.RTC;
-import static android.app.AlarmManager.RTC_WAKEUP;
 import static android.os.Build.VERSION.SDK_INT;
 import static android.os.Build.VERSION_CODES.O;
 import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_DEFAULT;
-import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_LOW;
-import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_MAX;
-import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_MIN;
-
-// import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY;
+import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY;
 
 /**
  * Central way to access all or single local notifications set by specific
@@ -92,67 +84,12 @@ public final class Manager {
      * @param receiver Receiver to handle the trigger event.
      */
     public Notification schedule (Request request, Class<?> receiver) {
-        Options options  = request.getOptions();
-        AlarmManager mgr = getAlarmMgr();
-
-        do {
-            Date date = request.getTriggerDate();
-
-            if (date == null)
-                continue;
-
-            Intent intent = new Intent(context, receiver)
-                    .setAction(request.getIdentifier())
-                    .putExtra(Request.EXTRA, request.getOccurrence())
-                    .putExtra(Options.EXTRA, options.toString());
-
-            if (!date.after(new Date()) && trigger(intent, receiver))
-                continue;
-
-            PendingIntent pi = PendingIntent.getBroadcast(
-                     context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
-
-            try {
-                switch (options.getPriority()) {
-                    case IMPORTANCE_MIN:
-                        mgr.setExact(RTC, date.getTime(), pi);
-                        break;
-                    case IMPORTANCE_MAX:
-                        mgr.setExactAndAllowWhileIdle(RTC_WAKEUP, date.getTime(), pi);
-                        break;
-                    default:
-                        mgr.setExact(RTC_WAKEUP, date.getTime(), pi);
-                        break;
-                }
-            } catch (Exception ignore) {
-                // Samsung devices have a known bug where a 500 alarms limit
-                // can crash the app
-            }
-
-        } while (request.moveNext());
-
-        return new Notification(context, options);
-    }
-
-    /**
-     * Trigger local notification specified by options.
-     *
-     * @param intent The intent to broadcast.
-     * @param cls    The broadcast class.
-     */
-    private boolean trigger (Intent intent, Class<?> cls) {
-        BroadcastReceiver receiver;
+        Options options    = request.getOptions();
+        Notification toast = new Notification(context, options);
 
-        try {
-            receiver = (BroadcastReceiver) cls.newInstance();
-        } catch (InstantiationException e) {
-            return false;
-        } catch (IllegalAccessException e) {
-            return false;
-        }
+        toast.schedule(request, receiver);
 
-        receiver.onReceive(context, intent);
-        return true;
+        return toast;
     }
 
     /**
@@ -459,33 +396,46 @@ public final class Manager {
     //     return options;
     // }
 
-    // /**
-    //  * Get existent local notification.
-    //  *
-    //  * @param id
-    //  *      Notification ID
-    //  */
-    // public Notification get(int id) {
-    //     Map<String, ?> alarms = getPrefs().getAll();
-    //     String notId          = Integer.toString(id);
-    //     JSONObject options;
+    /**
+     * Get local notification options.
+     *
+     * @param id Notification ID.
+     *
+     * @return null if could not found.
+     */
+    public Options getOptions(int id) {
+        SharedPreferences prefs = getPrefs();
+        String toastId          = Integer.toString(id);
 
-    //     if (!alarms.containsKey(notId))
-    //         return null;
+        if (!prefs.contains(toastId))
+            return null;
 
+        try {
+            String json     = prefs.getString(toastId, null);
+            JSONObject dict = new JSONObject(json);
 
-    //     try {
-    //         String json = alarms.get(notId).toString();
-    //         options = new JSONObject(json);
-    //     } catch (JSONException e) {
-    //         e.printStackTrace();
-    //         return null;
-    //     }
+            return new Options(context, dict);
+        } catch (JSONException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
 
-    //     Builder builder = new Builder(context, options);
+    /**
+     * Get existent local notification.
+     *
+     * @param id Notification ID.
+     *
+     * @return null if could not found.
+     */
+    public Notification get(int id) {
+        Options options = getOptions(id);
 
-    //     return builder.build();
-    // }
+        if (options == null)
+            return null;
+
+        return new Notification(context, options);
+    }
 
     // /**
     //  * Merge two JSON objects.
@@ -511,18 +461,11 @@ public final class Manager {
     //     return obj1;
     // }
 
-    // /**
-    //  * Shared private preferences for the application.
-    //  */
-    // private SharedPreferences getPrefs () {
-    //     return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
-    // }
-
     /**
-     * Alarm manager for the application.
+     * Shared private preferences for the application.
      */
-    private AlarmManager getAlarmMgr () {
-        return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+    private SharedPreferences getPrefs () {
+        return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
     }
 
     /**

+ 152 - 21
src/android/notification/Notification.java

@@ -24,15 +24,30 @@ package de.appplant.cordova.plugin.notification;
 
 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.content.SharedPreferences;
 import android.net.Uri;
 import android.support.v4.app.NotificationCompat;
+import android.support.v4.util.ArraySet;
+import android.support.v4.util.Pair;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+import static android.app.AlarmManager.RTC;
+import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_MAX;
+import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_MIN;
+
 /**
  * Wrapper class around OS notification class. Handles basic operations
  * like show, delete, cancel for a single local notification instance.
@@ -44,6 +59,9 @@ public final class Notification {
         ALL, SCHEDULED, TRIGGERED
     }
 
+    // Extra key for the id
+    public static final String EXTRA_ID = "NOTIFICATION_ID";
+
     // Key for private preferences
     static final String PREF_KEY = "LocalNotification";
 
@@ -105,7 +123,7 @@ public final class Notification {
     /**
      * If it's a repeating notification.
      */
-    public boolean isRepeating () {
+    private boolean isRepeating () {
         return getOptions().getTrigger().has("every");
     }
 
@@ -146,16 +164,102 @@ public final class Notification {
     //     return isScheduled() ? Type.SCHEDULED : Type.TRIGGERED;
     // }
 
+    /**
+     * Schedule the local notification.
+     *
+     * @param request Set of notification options.
+     * @param receiver Receiver to handle the trigger event.
+     */
+    void schedule(Request request, Class<?> receiver) {
+        List<Pair<Date, Intent>> intents = new ArrayList<Pair<Date, Intent>>();
+        Set<String> ids = new ArraySet<String>();
+        AlarmManager mgr = getAlarmMgr();
+
+        do {
+            Date date = request.getTriggerDate();
+
+            if (date == null)
+                continue;
+
+            Intent intent = new Intent(context, receiver)
+                    .setAction(PREF_KEY + "#" + request.getIdentifier())
+                    .putExtra(Notification.EXTRA_ID, options.getId())
+                    .putExtra(Request.EXTRA_OCCURRENCE, request.getOccurrence());
+
+            intents.add(new Pair<Date, Intent>(date, intent));
+        }
+        while (request.moveNext());
+
+        if (intents.isEmpty())
+            return;
+
+        Intent last = intents.get(intents.size() - 1).second;
+        last.putExtra(Request.EXTRA_LAST, true);
+
+        for (Pair<Date, Intent> pair : intents) {
+            Date date     = pair.first;
+            long time     = date.getTime();
+            Intent intent = pair.second;
+
+            if (!date.after(new Date()) && trigger(intent, receiver))
+                continue;
+
+            PendingIntent pi = PendingIntent.getBroadcast(
+                    context, 0, intent, FLAG_CANCEL_CURRENT);
+
+            try {
+                switch (options.getPriority()) {
+                    case IMPORTANCE_MIN:
+                        mgr.setExact(RTC, time, pi);
+                        break;
+                    case IMPORTANCE_MAX:
+                        mgr.setExactAndAllowWhileIdle(RTC_WAKEUP, time, pi);
+                        break;
+                    default:
+                        mgr.setExact(RTC_WAKEUP, time, pi);
+                        break;
+                }
+                ids.add(intent.getAction());
+            } catch (Exception ignore) {
+                // Samsung devices have a known bug where a 500 alarms limit
+                // can crash the app
+            }
+        }
+
+        persist(ids);
+    }
+
+    /**
+     * Trigger local notification specified by options.
+     *
+     * @param intent The intent to broadcast.
+     * @param cls    The broadcast class.
+     */
+    private boolean trigger (Intent intent, Class<?> cls) {
+        BroadcastReceiver receiver;
+
+        try {
+            receiver = (BroadcastReceiver) cls.newInstance();
+        } catch (InstantiationException e) {
+            return false;
+        } catch (IllegalAccessException e) {
+            return false;
+        }
+
+        receiver.onReceive(context, intent);
+        return true;
+    }
+
     /**
      * Clear the local notification without canceling repeating alarms.
      */
     public void clear () {
+        getNotMgr().cancel(getId());
 
-        // if (!isRepeating() && wasInThePast())
-        //     unpersist();
+        if (isRepeating())
+            return;
 
-        // if (!isRepeating())
-        //     getNotMgr().cancel(getId());
+        unpersist();
     }
 
     /**
@@ -167,31 +271,36 @@ public final class Notification {
      * method and cancel it.
      */
     public void cancel() {
-        // Intent intent = new Intent(context, receiver)
-        //         .setAction(options.getIdStr());
+        Set<String> actions = getPrefs().getStringSet(
+                "#" + options.getIdentifier(), null);
 
-        // PendingIntent pi = PendingIntent.
-        //         getBroadcast(context, 0, intent, 0);
+        unpersist();
+        getNotMgr().cancel(options.getId());
 
-        // getAlarmMgr().cancel(pi);
-        // getNotMgr().cancel(options.getId());
+        if (actions == null)
+            return;
 
-        // unpersist();
+        for (String action : actions) {
+            Intent intent = new Intent(action);
+
+            PendingIntent pi = PendingIntent.getBroadcast(
+                    context, 0, intent, 0);
+
+            if (pi != null) {
+                getAlarmMgr().cancel(pi);
+            }
+        }
     }
 
     /**
      * Present the local notification to user.
      */
     public void show () {
+
         if (builder == null)
             return;
 
-        String sound = builder.getExtras().getString(Options.EXTRA_SOUND);
-        Uri soundUri = Uri.parse(sound);
-
-        context.grantUriPermission("com.android.systemui", soundUri,
-                Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
+        grantPermissionToPlaySoundFromExternal();
         getNotMgr().notify(getId(), builder.build());
     }
 
@@ -231,11 +340,15 @@ public final class Notification {
      * Persist the information of this notification to the Android Shared
      * Preferences. This will allow the application to restore the notification
      * upon device reboot, app restart, retrieve notifications, aso.
+     *
+     * @param ids List of intent actions to persist.
      */
-    private void persist () {
+    private void persist (Set<String> ids) {
         SharedPreferences.Editor editor = getPrefs().edit();
+        String id = options.getIdentifier();
 
-        editor.putString(options.getIdentifier(), options.toString());
+        editor.putString(id, options.toString());
+        editor.putStringSet("#" + id, ids);
         editor.apply();
     }
 
@@ -244,11 +357,29 @@ public final class Notification {
      */
     private void unpersist () {
         SharedPreferences.Editor editor = getPrefs().edit();
+        String id = options.getIdentifier();
 
-        editor.remove(options.getIdentifier());
+        editor.remove(id);
+        editor.remove("#" + id);
         editor.apply();
     }
 
+    /**
+     * Since Android 7 the app will crash if an external process has no
+     * permission to access the referenced sound file.
+     */
+    private void grantPermissionToPlaySoundFromExternal() {
+        if (builder == null)
+            return;
+
+        String sound = builder.getExtras().getString(Options.EXTRA_SOUND);
+        Uri soundUri = Uri.parse(sound);
+
+        context.grantUriPermission(
+                "com.android.systemui", soundUri,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION);
+    }
+
     /**
      * Shared private preferences for the application.
      */

+ 0 - 3
src/android/notification/Options.java

@@ -55,9 +55,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
  */
 public final class Options {
 
-    // Key name for bundled extras
-    public static final String EXTRA = "NOTIFICATION_OPTIONS";
-
     // Key name for bundled sound extra
     static final String EXTRA_SOUND = "NOTIFICATION_SOUND";
 

+ 4 - 1
src/android/notification/Request.java

@@ -43,7 +43,10 @@ import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Un
 public final class Request {
 
     // Key name for bundled extras
-    static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA";
+    static final String EXTRA_OCCURRENCE = "NOTIFICATION_OCCURRENCE";
+
+    // Key name for bundled extras
+    public static final String EXTRA_LAST = "NOTIFICATION_LAST";
 
     // The options spec
     private final Options options;

+ 23 - 24
src/android/notification/activity/AbstractClickActivity.java

@@ -26,14 +26,13 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.appplant.cordova.plugin.notification.action.Action;
+import de.appplant.cordova.plugin.notification.Manager;
 import de.appplant.cordova.plugin.notification.Notification;
-import de.appplant.cordova.plugin.notification.Options;
 
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
 import static de.appplant.cordova.plugin.notification.action.Action.CLICK_ACTION_ID;
+import static de.appplant.cordova.plugin.notification.action.Action.EXTRA_ID;
 
 /**
  * Abstract content receiver activity for local notifications. Creates the
@@ -50,23 +49,16 @@ abstract public class AbstractClickActivity extends Activity {
     public void onCreate (Bundle state) {
         super.onCreate(state);
 
-        Intent intent   = getIntent();
-        Bundle bundle   = intent.getExtras();
-        Context context = getApplicationContext();
-
-        try {
-            String action   = bundle.getString(Action.EXTRA_ID, CLICK_ACTION_ID);
-            String data     = bundle.getString(Options.EXTRA);
-            JSONObject dict = new JSONObject(data);
-            Options options = new Options(context, dict);
+        Intent intent      = getIntent();
+        Bundle bundle      = intent.getExtras();
+        Context context    = getApplicationContext();
+        int toastId        = bundle.getInt(Notification.EXTRA_ID);
+        Notification toast = Manager.getInstance(context).get(toastId);
 
-            Notification notification =
-                    new Notification(context, options);
+        if (toast == null)
+            return;
 
-            onClick(action, notification);
-        } catch (JSONException e) {
-            e.printStackTrace();
-        }
+        onClick(toast, bundle);
     }
 
     /**
@@ -82,15 +74,22 @@ abstract public class AbstractClickActivity extends Activity {
     /**
      * Called when local notification was clicked by the user.
      *
-     * @param action       The name of the action.
      * @param notification Wrapper around the local notification.
+     * @param bundle The bundled extras.
      */
-    abstract public void onClick (String action, Notification notification);
+    abstract public void onClick (Notification notification, Bundle bundle);
+
+    /**
+     * The invoked action.
+     */
+    protected String getAction() {
+        return getIntent().getExtras().getString(EXTRA_ID, CLICK_ACTION_ID);
+    }
 
     /**
      * Launch main intent from package.
      */
-    public void launchApp() {
+    protected void launchApp() {
         Context context = getApplicationContext();
         String pkgName  = context.getPackageName();
 
@@ -99,7 +98,7 @@ abstract public class AbstractClickActivity extends Activity {
                 .getLaunchIntentForPackage(pkgName);
 
         intent.addFlags(
-                Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+                FLAG_ACTIVITY_REORDER_TO_FRONT | FLAG_ACTIVITY_SINGLE_TOP);
 
         context.startActivity(intent);
     }

+ 0 - 54
src/android/notification/activity/ClickActivity.java

@@ -1,54 +0,0 @@
-/*
- * Apache 2.0 License
- *
- * Copyright (c) Sebastian Katzer 2017
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apache License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://opensource.org/licenses/Apache-2.0/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- */
-
-package de.appplant.cordova.plugin.notification.activity;
-
-import de.appplant.cordova.plugin.notification.Notification;
-
-/**
- * The receiver activity is triggered when a notification is clicked by a user.
- * The activity calls the background callback and brings the launch intent
- * up to foreground.
- */
-public class ClickActivity extends AbstractClickActivity {
-
-    /**
-     * Called when local notification was clicked by the user. Will
-     * move the app to foreground.
-     *
-     * @param action       The name of the action.
-     * @param notification Wrapper around the local notification.
-     */
-    @Override
-    public void onClick(String action, Notification notification) {
-        launchApp();
-
-        if (notification.getOptions().isSticky())
-            return;
-
-        if (notification.isRepeating()) {
-            notification.clear();
-        } else {
-            notification.cancel();
-        }
-    }
-
-}

+ 10 - 18
src/android/notification/receiver/AbstractClearReceiver.java

@@ -26,11 +26,8 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
+import de.appplant.cordova.plugin.notification.Manager;
 import de.appplant.cordova.plugin.notification.Notification;
-import de.appplant.cordova.plugin.notification.Options;
 
 /**
  * Abstract delete receiver for local notifications. Creates the local
@@ -46,27 +43,22 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver {
      */
     @Override
     public void onReceive(Context context, Intent intent) {
-        Bundle bundle  = intent.getExtras();
-        String data    = bundle.getString(Options.EXTRA);
-
-        try {
-            JSONObject dict = new JSONObject(data);
-            Options options = new Options(context, dict);
+        Bundle bundle      = intent.getExtras();
+        int toastId        = bundle.getInt(Notification.EXTRA_ID);
+        Notification toast = Manager.getInstance(context).get(toastId);
 
-            Notification notification =
-                    new Notification(context, options);
+        if (toast == null)
+            return;
 
-            onClear(notification);
-        } catch (JSONException e) {
-            e.printStackTrace();
-        }
+        onClear(toast, bundle);
     }
 
     /**
      * Called when a local notification was cleared from outside of the app.
      *
-     * @param notification Wrapper around the local notification
+     * @param notification Wrapper around the local notification.
+     * @param bundle The bundled extras.
      */
-    abstract public void onClear (Notification notification);
+    abstract public void onClear (Notification notification, Bundle bundle);
 
 }

+ 18 - 22
src/android/notification/receiver/AbstractTriggerReceiver.java

@@ -26,10 +26,8 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import de.appplant.cordova.plugin.notification.Builder;
+import de.appplant.cordova.plugin.notification.Manager;
 import de.appplant.cordova.plugin.notification.Notification;
 import de.appplant.cordova.plugin.notification.Options;
 
@@ -47,39 +45,37 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
      */
     @Override
     public void onReceive(Context context, Intent intent) {
-        Bundle bundle  = intent.getExtras();
-        Options options;
-
-        try {
-            String data = bundle.getString(Options.EXTRA);
-            JSONObject dict = new JSONObject(data);
+        Bundle bundle   = intent.getExtras();
+        int toastId     = bundle.getInt(Notification.EXTRA_ID);
+        Options options = Manager.getInstance(context).getOptions(toastId);
 
-            options = new Options(context, dict);
-        } catch (JSONException e) {
-            e.printStackTrace();
+        if (options == null)
             return;
-        }
 
-        Builder builder           = new Builder(options);
-        Notification notification = buildNotification(builder);
-        boolean updated           = false;// notification.isUpdate(false);
+        Builder builder    = new Builder(options);
+        Notification toast = buildNotification(builder, bundle);
+
+        if (toast == null)
+            return;
 
-        onTrigger(notification, updated);
+        onTrigger(toast, bundle);
     }
 
     /**
      * Called when a local notification was triggered.
      *
-     * @param notification  Wrapper around the local notification
-     * @param updated       If an update has triggered or the original
+     * @param notification Wrapper around the local notification.
+     * @param bundle       The bundled extras.
      */
-    abstract public void onTrigger (Notification notification, boolean updated);
+    abstract public void onTrigger (Notification notification, Bundle bundle);
 
     /**
      * Build notification specified by options.
      *
-     * @param builder Notification builder
+     * @param builder Notification builder.
+     * @param bundle  The bundled extras.
      */
-    abstract public Notification buildNotification (Builder builder);
+    abstract public Notification buildNotification (Builder builder,
+                                                    Bundle bundle);
 
 }

+ 0 - 43
src/android/notification/receiver/ClearReceiver.java

@@ -1,43 +0,0 @@
-/*
- * Apache 2.0 License
- *
- * Copyright (c) Sebastian Katzer 2017
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apache License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://opensource.org/licenses/Apache-2.0/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- */
-
-package de.appplant.cordova.plugin.notification.receiver;
-
-import de.appplant.cordova.plugin.notification.Notification;
-
-/**
- * The clear intent receiver is triggered when the user clears a
- * notification manually. It un-persists the cleared notification from the
- * shared preferences.
- */
-public class ClearReceiver extends AbstractClearReceiver {
-
-    /**
-     * Called when a local notification was cleared from outside of the app.
-     *
-     * @param notification Wrapper around the local notification
-     */
-    @Override
-    public void onClear (Notification notification) {
-        notification.clear();
-    }
-
-}

+ 0 - 57
src/android/notification/receiver/TriggerReceiver.java

@@ -1,57 +0,0 @@
-/*
- * Apache 2.0 License
- *
- * Copyright (c) Sebastian Katzer 2017
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apache License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://opensource.org/licenses/Apache-2.0/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- */
-
-package de.appplant.cordova.plugin.notification.receiver;
-
-import de.appplant.cordova.plugin.notification.Builder;
-import de.appplant.cordova.plugin.notification.Notification;
-
-/**
- * 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.
- */
-public class TriggerReceiver extends AbstractTriggerReceiver {
-
-    /**
-     * Called when a local notification was triggered. Does present the local
-     * notification and re-schedule the alarm if necessary.
-     *
-     * @param notification Wrapper around the local notification
-     * @param updated      If an update has triggered or the original
-     */
-    @Override
-    public void onTrigger (Notification notification, boolean updated) {
-        notification.show();
-    }
-
-    /**
-     * Build notification specified by options.
-     *
-     * @param builder Notification builder
-     */
-    @Override
-    public Notification buildNotification (Builder builder) {
-        return builder.build();
-    }
-
-}