Commit cd78f87b by Sebastián Katzer

Android WIP

parent 795a2338
...@@ -21,9 +21,13 @@ ...@@ -21,9 +21,13 @@
package de.appplant.cordova.plugin.localnotification; package de.appplant.cordova.plugin.localnotification;
import android.os.Bundle;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.receiver.AbstractClearReceiver; 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 * The clear intent receiver is triggered when the user clears a
...@@ -35,11 +39,19 @@ public class ClearReceiver extends AbstractClearReceiver { ...@@ -35,11 +39,19 @@ public class ClearReceiver extends AbstractClearReceiver {
/** /**
* Called when a local notification was cleared from outside of the app. * 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 @Override
public void onClear (Notification notification) { public void onClear (Notification notification, Bundle bundle) {
notification.clear(); boolean isLast = bundle.getBoolean(EXTRA_LAST, false);
if (isLast) {
notification.cancel();
} else {
notification.clear();
}
LocalNotification.fireEvent("clear", notification); LocalNotification.fireEvent("clear", notification);
} }
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
package de.appplant.cordova.plugin.localnotification; package de.appplant.cordova.plugin.localnotification;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.RemoteInput; import android.support.v4.app.RemoteInput;
...@@ -29,9 +28,11 @@ import org.json.JSONException; ...@@ -29,9 +28,11 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
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.activity.AbstractClickActivity; 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 receiver activity is triggered when a notification is clicked by a user.
* The activity calls the background callback and brings the launch intent * The activity calls the background callback and brings the launch intent
...@@ -42,39 +43,65 @@ public class ClickActivity extends AbstractClickActivity { ...@@ -42,39 +43,65 @@ public class ClickActivity extends AbstractClickActivity {
/** /**
* Called when local notification was clicked by the user. * Called when local notification was clicked by the user.
* *
* @param action The name of the action.
* @param notification Wrapper around the local notification. * @param notification Wrapper around the local notification.
* @param bundle The bundled extras.
*/ */
@Override @Override
public void onClick(String action, Notification notification) { public void onClick(Notification notification, Bundle bundle) {
String action = getAction();
JSONObject data = new JSONObject(); 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) { setTextInput(action, data);
launchApp(); launchAppIf();
}
LocalNotification.fireEvent(action, notification, data); LocalNotification.fireEvent(action, notification, data);
if (notification.getOptions().isSticky()) if (notification.getOptions().isSticky())
return; return;
if (notification.isRepeating()) { if (isLast()) {
notification.clear();
} else {
notification.cancel(); 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);
}
} }
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
package de.appplant.cordova.plugin.localnotification; package de.appplant.cordova.plugin.localnotification;
import android.os.Bundle;
import de.appplant.cordova.plugin.notification.Builder; import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.receiver.AbstractTriggerReceiver; import de.appplant.cordova.plugin.notification.receiver.AbstractTriggerReceiver;
...@@ -37,28 +39,27 @@ public class TriggerReceiver extends AbstractTriggerReceiver { ...@@ -37,28 +39,27 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
* Called when a local notification was triggered. Does present the local * Called when a local notification was triggered. Does present the local
* notification, re-schedule the alarm if necessary and fire trigger event. * notification, re-schedule the alarm if necessary and fire trigger event.
* *
* @param notification Wrapper around the local notification * @param notification Wrapper around the local notification.
* @param updated If an update has triggered or the original * @param bundle The bundled extras.
*/ */
@Override @Override
public void onTrigger (Notification notification, boolean updated) { public void onTrigger (Notification notification, Bundle bundle) {
notification.show(); notification.show();
LocalNotification.fireEvent("trigger", notification);
if (!updated) {
LocalNotification.fireEvent("trigger", notification);
}
} }
/** /**
* Build notification specified by options. * Build notification specified by options.
* *
* @param builder Notification builder * @param builder Notification builder.
* @param bundle The bundled extras.
*/ */
@Override @Override
public Notification buildNotification (Builder builder) { public Notification buildNotification (Builder builder, Bundle bundle) {
return builder return builder
.setClickActivity(ClickActivity.class) .setClickActivity(ClickActivity.class)
.setClearReceiver(ClearReceiver.class) .setClearReceiver(ClearReceiver.class)
.setExtras(bundle)
.build(); .build();
} }
......
...@@ -53,6 +53,9 @@ public final class Builder { ...@@ -53,6 +53,9 @@ public final class Builder {
// Activity to handle the click event // Activity to handle the click event
private Class<?> clickActivity; private Class<?> clickActivity;
// Additional extras to merge into each intent
private Bundle extras;
/** /**
* Constructor * Constructor
* *
...@@ -84,6 +87,16 @@ public final class Builder { ...@@ -84,6 +87,16 @@ public final class Builder {
} }
/** /**
* 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. * Creates the notification with all its options passed through JS.
* *
* @return The final notification to display. * @return The final notification to display.
...@@ -99,7 +112,7 @@ public final class Builder { ...@@ -99,7 +112,7 @@ public final class Builder {
Uri sound = options.getSound(); Uri sound = options.getSound();
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putString(Options.EXTRA, options.toString()); extras.putInt(Notification.EXTRA_ID, options.getId());
extras.putString(Options.EXTRA_SOUND, sound.toString()); extras.putString(Options.EXTRA_SOUND, sound.toString());
builder = new NotificationCompat.Builder(context, Manager.CHANNEL_ID) builder = new NotificationCompat.Builder(context, Manager.CHANNEL_ID)
...@@ -266,8 +279,9 @@ public final class Builder { ...@@ -266,8 +279,9 @@ public final class Builder {
return; return;
Intent intent = new Intent(context, clearReceiver) Intent intent = new Intent(context, clearReceiver)
.putExtras(extras)
.setAction(options.getIdentifier()) .setAction(options.getIdentifier())
.putExtra(Options.EXTRA, options.toString()); .putExtra(Notification.EXTRA_ID, options.getId());
PendingIntent deleteIntent = PendingIntent.getBroadcast( PendingIntent deleteIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
...@@ -287,9 +301,10 @@ public final class Builder { ...@@ -287,9 +301,10 @@ public final class Builder {
return; return;
Intent intent = new Intent(context, clickActivity) Intent intent = new Intent(context, clickActivity)
.putExtra(Options.EXTRA, options.toString()) .putExtras(extras)
.putExtra(Options.EXTRA_LAUNCH, options.isLaunchingApp()) .putExtra(Notification.EXTRA_ID, options.getId())
.putExtra(Action.EXTRA_ID, Action.CLICK_ACTION_ID) .putExtra(Action.EXTRA_ID, Action.CLICK_ACTION_ID)
.putExtra(Options.EXTRA_LAUNCH, options.isLaunchingApp())
.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
int reqCode = new Random().nextInt(); int reqCode = new Random().nextInt();
...@@ -333,15 +348,16 @@ public final class Builder { ...@@ -333,15 +348,16 @@ public final class Builder {
*/ */
private PendingIntent getPendingIntentForAction (Action action) { private PendingIntent getPendingIntentForAction (Action action) {
Intent intent = new Intent(context, clickActivity) Intent intent = new Intent(context, clickActivity)
.putExtra(Options.EXTRA, options.toString()) .putExtras(extras)
.putExtra(Options.EXTRA_LAUNCH, action.isLaunchingApp()) .putExtra(Notification.EXTRA_ID, options.getId())
.putExtra(Action.EXTRA_ID, action.getId()) .putExtra(Action.EXTRA_ID, action.getId())
.putExtra(Options.EXTRA_LAUNCH, action.isLaunchingApp())
.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
int requestCode = new Random().nextInt(); int reqCode = new Random().nextInt();
return PendingIntent.getActivity( return PendingIntent.getActivity(
context, requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT); context, reqCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
} }
} }
...@@ -21,27 +21,19 @@ ...@@ -21,27 +21,19 @@
package de.appplant.cordova.plugin.notification; package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.SharedPreferences;
import android.support.v4.app.NotificationManagerCompat; 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.SDK_INT;
import static android.os.Build.VERSION_CODES.O; 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_DEFAULT;
import static android.support.v4.app.NotificationManagerCompat.IMPORTANCE_LOW; import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY;
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;
/** /**
* Central way to access all or single local notifications set by specific * Central way to access all or single local notifications set by specific
...@@ -92,67 +84,12 @@ public final class Manager { ...@@ -92,67 +84,12 @@ public final class Manager {
* @param receiver Receiver to handle the trigger event. * @param receiver Receiver to handle the trigger event.
*/ */
public Notification schedule (Request request, Class<?> receiver) { public Notification schedule (Request request, Class<?> receiver) {
Options options = request.getOptions(); Options options = request.getOptions();
AlarmManager mgr = getAlarmMgr(); Notification toast = new Notification(context, options);
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;
try { toast.schedule(request, receiver);
receiver = (BroadcastReceiver) cls.newInstance();
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
receiver.onReceive(context, intent); return toast;
return true;
} }
/** /**
...@@ -459,33 +396,46 @@ public final class Manager { ...@@ -459,33 +396,46 @@ public final class Manager {
// return options; // return options;
// } // }
// /** /**
// * Get existent local notification. * Get local notification options.
// * *
// * @param id * @param id Notification ID.
// * Notification ID *
// */ * @return null if could not found.
// public Notification get(int id) { */
// Map<String, ?> alarms = getPrefs().getAll(); public Options getOptions(int id) {
// String notId = Integer.toString(id); SharedPreferences prefs = getPrefs();
// JSONObject options; String toastId = Integer.toString(id);
// if (!alarms.containsKey(notId)) if (!prefs.contains(toastId))
// return null; return null;
try {
String json = prefs.getString(toastId, null);
JSONObject dict = new JSONObject(json);
// try { return new Options(context, dict);
// String json = alarms.get(notId).toString(); } catch (JSONException e) {
// options = new JSONObject(json); e.printStackTrace();
// } catch (JSONException e) { return null;
// 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. // * Merge two JSON objects.
...@@ -511,18 +461,11 @@ public final class Manager { ...@@ -511,18 +461,11 @@ public final class Manager {
// return obj1; // 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 () { private SharedPreferences getPrefs () {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE);
} }
/** /**
......
...@@ -24,15 +24,30 @@ package de.appplant.cordova.plugin.notification; ...@@ -24,15 +24,30 @@ package de.appplant.cordova.plugin.notification;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.net.Uri; import android.net.Uri;
import android.support.v4.app.NotificationCompat; 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.JSONException;
import org.json.JSONObject; 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 * 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.
...@@ -44,6 +59,9 @@ public final class Notification { ...@@ -44,6 +59,9 @@ public final class Notification {
ALL, SCHEDULED, TRIGGERED ALL, SCHEDULED, TRIGGERED
} }
// Extra key for the id
public static final String EXTRA_ID = "NOTIFICATION_ID";
// Key for private preferences // Key for private preferences
static final String PREF_KEY = "LocalNotification"; static final String PREF_KEY = "LocalNotification";
...@@ -105,7 +123,7 @@ public final class Notification { ...@@ -105,7 +123,7 @@ public final class Notification {
/** /**
* If it's a repeating notification. * If it's a repeating notification.
*/ */
public boolean isRepeating () { private boolean isRepeating () {
return getOptions().getTrigger().has("every"); return getOptions().getTrigger().has("every");
} }
...@@ -147,15 +165,101 @@ public final class Notification { ...@@ -147,15 +165,101 @@ public final class Notification {
// } // }
/** /**
* 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. * Clear the local notification without canceling repeating alarms.
*/ */
public void clear () { public void clear () {
getNotMgr().cancel(getId());
// if (!isRepeating() && wasInThePast()) if (isRepeating())
// unpersist(); return;
// if (!isRepeating()) unpersist();
// getNotMgr().cancel(getId());
} }
/** /**
...@@ -167,31 +271,36 @@ public final class Notification { ...@@ -167,31 +271,36 @@ public final class Notification {
* method and cancel it. * method and cancel it.
*/ */
public void cancel() { public void cancel() {
// Intent intent = new Intent(context, receiver) Set<String> actions = getPrefs().getStringSet(
// .setAction(options.getIdStr()); "#" + options.getIdentifier(), null);
// PendingIntent pi = PendingIntent. unpersist();
// getBroadcast(context, 0, intent, 0); getNotMgr().cancel(options.getId());
// getAlarmMgr().cancel(pi); if (actions == null)
// getNotMgr().cancel(options.getId()); 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. * Present the local notification to user.
*/ */
public void show () { public void show () {
if (builder == null) if (builder == null)
return; return;
String sound = builder.getExtras().getString(Options.EXTRA_SOUND); grantPermissionToPlaySoundFromExternal();
Uri soundUri = Uri.parse(sound);
context.grantUriPermission("com.android.systemui", soundUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
getNotMgr().notify(getId(), builder.build()); getNotMgr().notify(getId(), builder.build());
} }
...@@ -231,11 +340,15 @@ public final class Notification { ...@@ -231,11 +340,15 @@ public final class Notification {
* Persist the information of this notification to the Android Shared * Persist the information of this notification to the Android Shared
* Preferences. This will allow the application to restore the notification * Preferences. This will allow the application to restore the notification
* upon device reboot, app restart, retrieve notifications, aso. * 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(); 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(); editor.apply();
} }
...@@ -244,12 +357,30 @@ public final class Notification { ...@@ -244,12 +357,30 @@ public final class Notification {
*/ */
private void unpersist () { private void unpersist () {
SharedPreferences.Editor editor = getPrefs().edit(); SharedPreferences.Editor editor = getPrefs().edit();
String id = options.getIdentifier();
editor.remove(options.getIdentifier()); editor.remove(id);
editor.remove("#" + id);
editor.apply(); 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. * Shared private preferences for the application.
*/ */
private SharedPreferences getPrefs () { private SharedPreferences getPrefs () {
......
...@@ -55,9 +55,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; ...@@ -55,9 +55,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
*/ */
public final class Options { public final class Options {
// Key name for bundled extras
public static final String EXTRA = "NOTIFICATION_OPTIONS";
// Key name for bundled sound extra // Key name for bundled sound extra
static final String EXTRA_SOUND = "NOTIFICATION_SOUND"; static final String EXTRA_SOUND = "NOTIFICATION_SOUND";
......
...@@ -43,7 +43,10 @@ import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Un ...@@ -43,7 +43,10 @@ import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Un
public final 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_OCCURRENCE = "NOTIFICATION_OCCURRENCE";
// Key name for bundled extras
public static final String EXTRA_LAST = "NOTIFICATION_LAST";
// The options spec // The options spec
private final Options options; private final Options options;
......
...@@ -26,14 +26,13 @@ import android.content.Context; ...@@ -26,14 +26,13 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.json.JSONException; import de.appplant.cordova.plugin.notification.Manager;
import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.action.Action;
import de.appplant.cordova.plugin.notification.Notification; 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.CLICK_ACTION_ID;
import static de.appplant.cordova.plugin.notification.action.Action.EXTRA_ID;
/** /**
* Abstract content receiver activity for local notifications. Creates the * Abstract content receiver activity for local notifications. Creates the
...@@ -50,23 +49,16 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -50,23 +49,16 @@ abstract public class AbstractClickActivity extends Activity {
public void onCreate (Bundle state) { public void onCreate (Bundle state) {
super.onCreate(state); super.onCreate(state);
Intent intent = getIntent(); Intent intent = getIntent();
Bundle bundle = intent.getExtras(); Bundle bundle = intent.getExtras();
Context context = getApplicationContext(); Context context = getApplicationContext();
int toastId = bundle.getInt(Notification.EXTRA_ID);
try { Notification toast = Manager.getInstance(context).get(toastId);
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);
Notification notification = if (toast == null)
new Notification(context, options); return;
onClick(action, notification); onClick(toast, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
} }
/** /**
...@@ -82,15 +74,22 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -82,15 +74,22 @@ abstract public class AbstractClickActivity extends Activity {
/** /**
* Called when local notification was clicked by the user. * Called when local notification was clicked by the user.
* *
* @param action The name of the action.
* @param notification Wrapper around the local notification. * @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. * Launch main intent from package.
*/ */
public void launchApp() { protected void launchApp() {
Context context = getApplicationContext(); Context context = getApplicationContext();
String pkgName = context.getPackageName(); String pkgName = context.getPackageName();
...@@ -99,7 +98,7 @@ abstract public class AbstractClickActivity extends Activity { ...@@ -99,7 +98,7 @@ abstract public class AbstractClickActivity extends Activity {
.getLaunchIntentForPackage(pkgName); .getLaunchIntentForPackage(pkgName);
intent.addFlags( 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); context.startActivity(intent);
} }
......
/*
* 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();
}
}
}
...@@ -26,11 +26,8 @@ import android.content.Context; ...@@ -26,11 +26,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.json.JSONException; import de.appplant.cordova.plugin.notification.Manager;
import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.Notification; import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/** /**
* Abstract delete receiver for local notifications. Creates the local * Abstract delete receiver for local notifications. Creates the local
...@@ -46,27 +43,22 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver { ...@@ -46,27 +43,22 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver {
*/ */
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras(); Bundle bundle = intent.getExtras();
String data = bundle.getString(Options.EXTRA); int toastId = bundle.getInt(Notification.EXTRA_ID);
Notification toast = Manager.getInstance(context).get(toastId);
try {
JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Notification notification = if (toast == null)
new Notification(context, options); return;
onClear(notification); onClear(toast, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
} }
/** /**
* Called when a local notification was cleared from outside of the app. * 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);
} }
...@@ -26,10 +26,8 @@ import android.content.Context; ...@@ -26,10 +26,8 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; 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.Builder;
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;
...@@ -47,39 +45,37 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver { ...@@ -47,39 +45,37 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
*/ */
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras(); Bundle bundle = intent.getExtras();
Options options; int toastId = bundle.getInt(Notification.EXTRA_ID);
Options options = Manager.getInstance(context).getOptions(toastId);
try {
String data = bundle.getString(Options.EXTRA);
JSONObject dict = new JSONObject(data);
options = new Options(context, dict); if (options == null)
} catch (JSONException e) {
e.printStackTrace();
return; return;
}
Builder builder = new Builder(options); Builder builder = new Builder(options);
Notification notification = buildNotification(builder); Notification toast = buildNotification(builder, bundle);
boolean updated = false;// notification.isUpdate(false);
if (toast == null)
return;
onTrigger(notification, updated); onTrigger(toast, bundle);
} }
/** /**
* Called when a local notification was triggered. * Called when a local notification was triggered.
* *
* @param notification Wrapper around the local notification * @param notification Wrapper around the local notification.
* @param updated If an update has triggered or the original * @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. * 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);
} }
/*
* 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();
}
}
/*
* 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();
}
}
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