Explorar o código

Initial support for actions on Android

Sebastián Katzer %!s(int64=8) %!d(string=hai) anos
pai
achega
c52bd8c865

+ 8 - 0
plugin.xml

@@ -195,6 +195,14 @@
             src="src/android/notification/util/AssetUtil.java"
             target-dir="src/de/appplant/cordova/plugin/notification/util" />
 
+        <source-file
+            src="src/android/notification/Action.java"
+            target-dir="src/de/appplant/cordova/plugin/notification" />
+
+        <source-file
+            src="src/android/notification/ActionGroup.java"
+            target-dir="src/de/appplant/cordova/plugin/notification" />
+
         <source-file
             src="src/android/notification/Builder.java"
             target-dir="src/de/appplant/cordova/plugin/notification" />

+ 18 - 0
src/android/LocalNotification.java

@@ -37,6 +37,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.lang.Exception;
 
+import de.appplant.cordova.plugin.notification.ActionGroup;
 import de.appplant.cordova.plugin.notification.Manager;
 import de.appplant.cordova.plugin.notification.Notification;
 import de.appplant.cordova.plugin.notification.Options;
@@ -145,6 +146,10 @@ public class LocalNotification extends CordovaPlugin {
                 if (action.equalsIgnoreCase("request")) {
                     request(command);
                 } else
+                if (action.equalsIgnoreCase("actions")) {
+                    actions(args.optJSONObject(0));
+                    command.success();
+                } else
                 if (action.equalsIgnoreCase("schedule")) {
                     schedule(args);
                     command.success();
@@ -234,6 +239,19 @@ public class LocalNotification extends CordovaPlugin {
         check(command);
     }
 
+    /**
+     * Register action group.
+     *
+     * @param args The action group spec.
+     */
+    private void actions (JSONObject args) {
+        ActionGroup group = ActionGroup.parse(cordova.getActivity(), args);
+
+        if (group != null) {
+            ActionGroup.register(group);
+        }
+    }
+
     /**
      * Schedule multiple local notifications.
      *

+ 114 - 0
src/android/notification/Action.java

@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import org.json.JSONObject;
+
+import de.appplant.cordova.plugin.notification.util.AssetUtil;
+
+/**
+ * Holds the icon and title components that would be used in a
+ * NotificationCompat.Action object. Does not include the PendingIntent so
+ * that it may be generated each time the notification is built. Necessary to
+ * compensate for missing functionality in the support library.
+ */
+final class Action {
+
+    // Key name for bundled extras
+    static final String EXTRA = "NOTIFICATION_ACTION_ID";
+
+    // The ID of the action
+    private final String id;
+
+    // The title for the action
+    private final String title;
+
+    // The icon for the action
+    private final int icon;
+
+    // The launch flag for the action
+    private final boolean launch;
+
+    /**
+     * Structure to encapsulate a named action that can be shown as part of
+     * this notification.
+     *
+     * @param context The application context.
+     * @param options The action options.
+     */
+    Action (Context context, JSONObject options) {
+        this.icon  = parseIcon(context, options.optString("icon"));
+        this.title = options.optString("title");
+        this.id    = options.optString("id", title);
+        this.launch = options.optBoolean("launch", false);
+    }
+
+    /**
+     * Gets the ID for the action.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Gets the Title for the action.
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Gets the icon for the action.
+     */
+    public int getIcon() {
+        return icon;
+    }
+
+    /**
+     * Gets the value of the launch flag.
+     */
+    public boolean isLaunchingApp() {
+        return launch;
+    }
+
+    /**
+     * Parse the uri into a resource id.
+     *
+     * @param context The application context.
+     * @param resPath The resource path.
+     *
+     * @return The resource id.
+     */
+    private int parseIcon(Context context, String resPath) {
+        AssetUtil assets = AssetUtil.getInstance(context);
+        int resId        = assets.getResId(resPath);
+
+        if (resId == 0) {
+            resId = android.R.drawable.screen_background_dark;
+        }
+
+        return resId;
+    }
+
+}

+ 127 - 0
src/android/notification/ActionGroup.java

@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+import android.content.Context;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public final class ActionGroup {
+
+    // Default action group id
+    private static final String GENERAL_ACTION_GROUP = "DEFAULT_GROUP";
+
+    // Saves all groups for later lookup.
+    private static final Map<String, ActionGroup> groups =
+            new HashMap<String, ActionGroup>();
+
+    // The ID of the action group.
+    private final String id;
+
+    // List of actions
+    private final Action[] actions;
+
+    /**
+     * Lookup the action groups with the specified group id.
+     *
+     * @param id The ID of the action group to find.
+     *
+     * @return Null if no group was found.
+     */
+    static ActionGroup lookup (String id) {
+        return groups.get(id);
+    }
+
+    /**
+     * Register the action group for later lookup.
+     *
+     * @param group The action group to register.
+     */
+    public static void register (ActionGroup group) {
+        if (!group.getId().equalsIgnoreCase(GENERAL_ACTION_GROUP)) {
+            groups.put(group.getId(), group);
+        }
+    }
+
+    /**
+     * Creates an action group by parsing the specified action specs.
+     *
+     * @param spec The action group spec containing the id and list of actions.
+     *
+     * @return A new action group.
+     */
+    public static ActionGroup parse (Context context, JSONObject spec) {
+        String id = spec.optString("actionGroupId", GENERAL_ACTION_GROUP);
+        JSONArray list = spec.optJSONArray("actions");
+
+        if (list == null || list.length() == 0)
+            return null;
+
+        List<Action> actions = new ArrayList<Action>(list.length());
+
+        for (int i = 0; i < list.length(); i++) {
+            JSONObject opts = list.optJSONObject(i);
+
+            if (!opts.optString("type", "button").equals("button"))
+                continue;
+
+            actions.add(new Action(context, opts));
+        }
+
+        if (actions.isEmpty())
+            return null;
+
+        return new ActionGroup(id, actions.toArray(new Action[actions.size()]));
+    }
+
+    /**
+     * Creates an action group.
+     *
+     * @param id      The ID of the group.
+     * @param actions The list of actions.
+     */
+    private ActionGroup(String id, Action[] actions) {
+        this.id      = id;
+        this.actions = actions;
+    }
+
+    /**
+     * Gets the action group id.
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Gets the action list.
+     */
+    public Action[] getActions() {
+        return actions;
+    }
+
+}

+ 38 - 1
src/android/notification/Builder.java

@@ -37,7 +37,7 @@ import de.appplant.cordova.plugin.notification.receiver.ClearReceiver;
  * Builder class for local notifications. Build fully configured local
  * notification specified by JSON object passed from JS side.
  */
-public class Builder {
+public final class Builder {
 
     // Application context passed by constructor
     private final Context context;
@@ -127,6 +127,7 @@ public class Builder {
         }
 
         applyStyle(builder);
+        applyActions(builder);
         applyDeleteReceiver(builder);
         applyContentReceiver(builder);
 
@@ -221,4 +222,40 @@ public class Builder {
         builder.setContentIntent(contentIntent);
     }
 
+    /**
+     * Add all actions to the builder if there are any actions.
+     *
+     * @param builder Local notification builder instance
+     */
+    private void applyActions (NotificationCompat.Builder builder) {
+        Action[] actions = options.getActions();
+
+        if (actions == null || actions.length == 0)
+            return;
+
+        for (Action action : actions) {
+            builder.addAction(
+                    action.getIcon(), action.getTitle(),
+                    getPendingIntentForAction(action));
+        }
+    }
+
+    /**
+     * Returns a new PendingIntent for a notification action, including the
+     * action's identifier.
+     *
+     * @param action Notification action needing the PendingIntent
+     */
+    private PendingIntent getPendingIntentForAction (Action action) {
+        Intent intent = new Intent(context, clickActivity)
+                .putExtra(Options.EXTRA, options.toString())
+                .putExtra(Action.EXTRA, action.getId())
+                .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+
+        int requestCode = new Random().nextInt();
+
+        return PendingIntent.getActivity(
+                context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
 }

+ 1 - 1
src/android/notification/Manager.java

@@ -51,7 +51,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
  * state like triggered or scheduled. Offers shortcut ways to schedule,
  * cancel or clear local notifications.
  */
-public class Manager {
+public final class Manager {
 
     // Context passed through constructor and used for notification builder.
     private Context context;

+ 1 - 1
src/android/notification/Notification.java

@@ -43,7 +43,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
  * Wrapper class around OS notification class. Handles basic operations
  * like show, delete, cancel for a single local notification instance.
  */
-public class Notification {
+public final class Notification {
 
     // Used to differ notifications by their life cycle state
     public enum Type {

+ 25 - 1
src/android/notification/Options.java

@@ -49,7 +49,7 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
  * possible option values. Class provides simple readers and more advanced
  * methods to convert independent values into platform specific values.
  */
-public class Options {
+public final class Options {
 
     // Key name for bundled extras
     public static final String EXTRA = "NOTIFICATION_OPTIONS";
@@ -518,6 +518,30 @@ public class Options {
         return options.optBoolean("silent", false);
     }
 
+    /**
+     * Gets the list of actions to display.
+     */
+    Action[] getActions() {
+        String groupId    = options.optString("actionGroupId", null);
+        JSONArray actions = options.optJSONArray("actions");
+        ActionGroup group = null;
+
+        if (actions != null && actions.length() > 0) {
+            group = ActionGroup.parse(context, options);
+        }
+
+        if (group == null && groupId != null) {
+            group = ActionGroup.lookup(groupId);
+        }
+
+        if (group != null) {
+            ActionGroup.register(group);
+            return group.getActions();
+        }
+
+        return null;
+    }
+
     /**
      * Gets the raw trigger spec as provided by the user.
      */

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

@@ -36,7 +36,7 @@ import de.appplant.cordova.plugin.notification.trigger.IntervalTrigger;
 
 import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Unit;
 
-public class Request {
+public final class Request {
 
     // Key name for bundled extras
     static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA";

+ 1 - 1
src/android/notification/util/AssetUtil.java

@@ -48,7 +48,7 @@ import java.util.UUID;
  * within the asset resources. And res:// means a resource from the native
  * res folder. Remote assets are accessible via http:// for example.
  */
-public class AssetUtil {
+public final class AssetUtil {
 
     // Name of the storage folder
     private static final String STORAGE_FOLDER = "/localnotification";

+ 1 - 1
src/ios/APPLocalNotification.h

@@ -36,7 +36,7 @@
 - (void) request:(CDVInvokedUrlCommand*)command;
 
 // Register/update an action group
-- (void) registerCategory:(CDVInvokedUrlCommand*)command;
+- (void) actions:(CDVInvokedUrlCommand*)command;
 
 // Schedule notifications
 - (void) schedule:(CDVInvokedUrlCommand*)command;

+ 1 - 1
src/ios/APPLocalNotification.m

@@ -432,7 +432,7 @@
  *
  * @return [ Void ]
  */
-- (void) registerCategory:(CDVInvokedUrlCommand *)command
+- (void) actions:(CDVInvokedUrlCommand *)command
 {
     [self.commandDelegate runInBackground:^{
         NSDictionary* options = command.arguments[0];

+ 1 - 1
www/local-notification-core.js

@@ -331,7 +331,7 @@ exports.getTriggered = function (callback, scope) {
  */
 exports.addActionGroup = function (id, actions, callback, scope) {
     var config = { actionGroupId: id, actions: actions };
-    this.exec('registerCategory', config, callback, scope);
+    this.exec('actions', config, callback, scope);
 };
 
 /**