Commit c52bd8c8 by Sebastián Katzer

Initial support for actions on Android

parent 46d71294
...@@ -196,6 +196,14 @@ ...@@ -196,6 +196,14 @@
target-dir="src/de/appplant/cordova/plugin/notification/util" /> target-dir="src/de/appplant/cordova/plugin/notification/util" />
<source-file <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" src="src/android/notification/Builder.java"
target-dir="src/de/appplant/cordova/plugin/notification" /> target-dir="src/de/appplant/cordova/plugin/notification" />
......
...@@ -37,6 +37,7 @@ import java.util.ArrayList; ...@@ -37,6 +37,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.lang.Exception; import java.lang.Exception;
import de.appplant.cordova.plugin.notification.ActionGroup;
import de.appplant.cordova.plugin.notification.Manager; import de.appplant.cordova.plugin.notification.Manager;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options; import de.appplant.cordova.plugin.notification.Options;
...@@ -145,6 +146,10 @@ public class LocalNotification extends CordovaPlugin { ...@@ -145,6 +146,10 @@ public class LocalNotification extends CordovaPlugin {
if (action.equalsIgnoreCase("request")) { if (action.equalsIgnoreCase("request")) {
request(command); request(command);
} else } else
if (action.equalsIgnoreCase("actions")) {
actions(args.optJSONObject(0));
command.success();
} else
if (action.equalsIgnoreCase("schedule")) { if (action.equalsIgnoreCase("schedule")) {
schedule(args); schedule(args);
command.success(); command.success();
...@@ -235,6 +240,19 @@ public class LocalNotification extends CordovaPlugin { ...@@ -235,6 +240,19 @@ public class LocalNotification extends CordovaPlugin {
} }
/** /**
* 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. * Schedule multiple local notifications.
* *
* @param notifications The notifications to schedule. * @param notifications The notifications to schedule.
......
/*
* 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;
}
}
\ No newline at end of file
/*
* 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;
}
}
\ No newline at end of file
...@@ -37,7 +37,7 @@ import de.appplant.cordova.plugin.notification.receiver.ClearReceiver; ...@@ -37,7 +37,7 @@ import de.appplant.cordova.plugin.notification.receiver.ClearReceiver;
* Builder class for local notifications. Build fully configured local * Builder class for local notifications. Build fully configured local
* notification specified by JSON object passed from JS side. * notification specified by JSON object passed from JS side.
*/ */
public class Builder { public final class Builder {
// Application context passed by constructor // Application context passed by constructor
private final Context context; private final Context context;
...@@ -127,6 +127,7 @@ public class Builder { ...@@ -127,6 +127,7 @@ public class Builder {
} }
applyStyle(builder); applyStyle(builder);
applyActions(builder);
applyDeleteReceiver(builder); applyDeleteReceiver(builder);
applyContentReceiver(builder); applyContentReceiver(builder);
...@@ -221,4 +222,40 @@ public class Builder { ...@@ -221,4 +222,40 @@ public class Builder {
builder.setContentIntent(contentIntent); 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);
}
} }
...@@ -51,7 +51,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver; ...@@ -51,7 +51,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
* state like triggered or scheduled. Offers shortcut ways to schedule, * state like triggered or scheduled. Offers shortcut ways to schedule,
* cancel or clear local notifications. * cancel or clear local notifications.
*/ */
public class Manager { public final class Manager {
// Context passed through constructor and used for notification builder. // Context passed through constructor and used for notification builder.
private Context context; private Context context;
......
...@@ -43,7 +43,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver; ...@@ -43,7 +43,7 @@ import de.appplant.cordova.plugin.notification.receiver.TriggerReceiver;
* Wrapper class around OS notification class. Handles basic operations * Wrapper class around OS notification class. Handles basic operations
* like show, delete, cancel for a single local notification instance. * 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 // Used to differ notifications by their life cycle state
public enum Type { public enum Type {
......
...@@ -49,7 +49,7 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; ...@@ -49,7 +49,7 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
* possible option values. Class provides simple readers and more advanced * possible option values. Class provides simple readers and more advanced
* methods to convert independent values into platform specific values. * methods to convert independent values into platform specific values.
*/ */
public class Options { public final class Options {
// Key name for bundled extras // Key name for bundled extras
public static final String EXTRA = "NOTIFICATION_OPTIONS"; public static final String EXTRA = "NOTIFICATION_OPTIONS";
...@@ -519,6 +519,30 @@ public class Options { ...@@ -519,6 +519,30 @@ public class Options {
} }
/** /**
* 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. * Gets the raw trigger spec as provided by the user.
*/ */
public JSONObject getTrigger() { public JSONObject getTrigger() {
......
...@@ -36,7 +36,7 @@ import de.appplant.cordova.plugin.notification.trigger.IntervalTrigger; ...@@ -36,7 +36,7 @@ import de.appplant.cordova.plugin.notification.trigger.IntervalTrigger;
import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Unit; import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Unit;
public class Request { public final class Request {
// Key name for bundled extras // Key name for bundled extras
static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA"; static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA";
......
...@@ -48,7 +48,7 @@ import java.util.UUID; ...@@ -48,7 +48,7 @@ import java.util.UUID;
* within the asset resources. And res:// means a resource from the native * within the asset resources. And res:// means a resource from the native
* res folder. Remote assets are accessible via http:// for example. * res folder. Remote assets are accessible via http:// for example.
*/ */
public class AssetUtil { public final class AssetUtil {
// Name of the storage folder // Name of the storage folder
private static final String STORAGE_FOLDER = "/localnotification"; private static final String STORAGE_FOLDER = "/localnotification";
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
- (void) request:(CDVInvokedUrlCommand*)command; - (void) request:(CDVInvokedUrlCommand*)command;
// Register/update an action group // Register/update an action group
- (void) registerCategory:(CDVInvokedUrlCommand*)command; - (void) actions:(CDVInvokedUrlCommand*)command;
// Schedule notifications // Schedule notifications
- (void) schedule:(CDVInvokedUrlCommand*)command; - (void) schedule:(CDVInvokedUrlCommand*)command;
......
...@@ -432,7 +432,7 @@ ...@@ -432,7 +432,7 @@
* *
* @return [ Void ] * @return [ Void ]
*/ */
- (void) registerCategory:(CDVInvokedUrlCommand *)command - (void) actions:(CDVInvokedUrlCommand *)command
{ {
[self.commandDelegate runInBackground:^{ [self.commandDelegate runInBackground:^{
NSDictionary* options = command.arguments[0]; NSDictionary* options = command.arguments[0];
......
...@@ -331,7 +331,7 @@ exports.getTriggered = function (callback, scope) { ...@@ -331,7 +331,7 @@ exports.getTriggered = function (callback, scope) {
*/ */
exports.addActionGroup = function (id, actions, callback, scope) { exports.addActionGroup = function (id, actions, callback, scope) {
var config = { actionGroupId: id, actions: actions }; var config = { actionGroupId: id, actions: actions };
this.exec('registerCategory', config, callback, scope); this.exec('actions', config, callback, scope);
}; };
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment