Commit c52bd8c8 by Sebastián Katzer

Initial support for actions on Android

parent 46d71294
......@@ -196,6 +196,14 @@
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" />
......
......@@ -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();
......@@ -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.
*
* @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;
* 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);
}
}
......@@ -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;
......
......@@ -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 {
......
......@@ -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";
......@@ -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.
*/
public JSONObject getTrigger() {
......
......@@ -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";
......
......@@ -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";
......
......@@ -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;
......
......@@ -432,7 +432,7 @@
*
* @return [ Void ]
*/
- (void) registerCategory:(CDVInvokedUrlCommand *)command
- (void) actions:(CDVInvokedUrlCommand *)command
{
[self.commandDelegate runInBackground:^{
NSDictionary* options = command.arguments[0];
......
......@@ -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);
};
/**
......
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