Commit 6e0cb626 by Sebastián Katzer

WIP Android

parent 8366689e
{
"name": "cordova-plugin-local-notifications",
"version": "0.0.0",
"version": "0.0.9",
"description": "Schedules and queries for local notifications",
"cordova": {
"id": "cordova-plugin-local-notifications",
......
......@@ -24,7 +24,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-local-notifications"
version="0.0.0">
version="0.9.0">
<name>LocalNotification</name>
......@@ -44,6 +44,7 @@
<engine name="cordova-plugman" version=">=4.3.0" />
<engine name="cordova-windows" version=">=4.2.0" />
<engine name="apple-ios" version=">=10.0.0" />
<engine name="cordova-android" version=">=6.0.0" />
</engines>
<!-- dependencies -->
......@@ -66,7 +67,6 @@
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="LocalNotification">
<param name="ios-package" value="APPLocalNotification" onload="true" />
......@@ -91,13 +91,11 @@
<header-file src="src/ios/UNNotificationRequest+APPLocalNotification.h" />
<source-file src="src/ios/UNNotificationRequest+APPLocalNotification.m" />
</platform>
<!-- android -->
<!-- <platform name="android">
<framework src="com.android.support:support-v4:+" value="gradle" />
<platform name="android">
<framework src="com.android.support:support-v4:26.+" value="gradle" />
<config-file target="res/xml/config.xml" parent="/*">
<feature name="LocalNotification">
......@@ -106,7 +104,6 @@
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<receiver
android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver"
android:exported="false" />
......@@ -118,33 +115,19 @@
<activity
android:name="de.appplant.cordova.plugin.localnotification.ClickActivity"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay"
android:exported="false" />
<receiver
android:name="de.appplant.cordova.plugin.notification.TriggerReceiver"
android:exported="false" />
<receiver
android:name="de.appplant.cordova.plugin.notification.ClearReceiver"
android:theme="@android:style/Theme.Translucent"
android:exported="false" />
<!--
<receiver android:name="de.appplant.cordova.plugin.localnotification.RestoreReceiver" android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity
android:name="de.appplant.cordova.plugin.notification.ClickActivity"
android:launchMode="singleInstance"
android:theme="@android:style/Theme.NoDisplay"
android:exported="false" />
-->
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</config-file>
......@@ -163,41 +146,57 @@
<source-file
src="src/android/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/localnotification" />
<!--
<source-file
src="src/android/RestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/localnotification" />
-->
<source-file
src="src/android/notification/activity/AbstractClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification/activity" />
<source-file
src="src/android/notification/AbstractClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/activity/ClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification/activity" />
<source-file
src="src/android/notification/AbstractClickActivity.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/AbstractClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<!--
<source-file
src="src/android/notification/receiver/AbstractRestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
-->
<source-file
src="src/android/notification/receiver/AbstractTriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file
src="src/android/notification/AbstractRestoreReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file
src="src/android/notification/AbstractTriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/receiver/TriggerReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification/receiver" />
<source-file
src="src/android/notification/AssetUtil.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/DateTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file
src="src/android/notification/Builder.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/IntervalTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file
src="src/android/notification/ClearReceiver.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
src="src/android/notification/trigger/MatchTrigger.java"
target-dir="src/de/appplant/cordova/plugin/notification/trigger" />
<source-file
src="src/android/notification/ClickActivity.java"
src="src/android/notification/util/AssetUtil.java"
target-dir="src/de/appplant/cordova/plugin/notification/util" />
<source-file
src="src/android/notification/Builder.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
<source-file
......@@ -213,17 +212,12 @@
target-dir="src/de/appplant/cordova/plugin/notification" />
<source-file
src="src/android/notification/TriggerReceiver.java"
src="src/android/notification/Request.java"
target-dir="src/de/appplant/cordova/plugin/notification" />
</platform> -->
</platform>
<!-- windows -->
<platform name="windows">
<config-file target="config.xml" parent="/*" >
<preference name="WindowsToastCapable" value="true" />
</config-file>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x86" arch="x86" custom="true"/>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="x64" arch="x64" custom="true"/>
<framework src="src/windows/lib.UW/ARM/LocalNotificationProxy.winmd" target-dir="ARM" arch="ARM" custom="true"/>
......
......@@ -29,13 +29,12 @@ import de.appplant.cordova.plugin.notification.Notification;
* notification manually. It un-persists the cleared notification from the
* shared preferences.
*/
public class ClearReceiver extends de.appplant.cordova.plugin.notification.ClearReceiver {
public class ClearReceiver extends de.appplant.cordova.plugin.notification.receiver.ClearReceiver {
/**
* 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
*/
@Override
public void onClear (Notification notification) {
......
......@@ -21,22 +21,19 @@
package de.appplant.cordova.plugin.localnotification;
import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.TriggerReceiver;
/**
* 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 de.appplant.cordova.plugin.notification.ClickActivity {
public class ClickActivity extends de.appplant.cordova.plugin.notification.activity.ClickActivity {
/**
* Called when local notification was clicked by the user.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
@Override
public void onClick(Notification notification) {
......@@ -44,24 +41,11 @@ public class ClickActivity extends de.appplant.cordova.plugin.notification.Click
super.onClick(notification);
if (notification.getOptions().isOngoing())
return;
// if (notification.getOptions().isOngoing())
// return;
String event = notification.isRepeating() ? "clear" : "cancel";
LocalNotification.fireEvent(event, notification);
}
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
@Override
public Notification buildNotification (Builder builder) {
return builder
.setTriggerReceiver(TriggerReceiver.class)
.build();
// String event = notification.isRepeating() ? "clear" : "cancel";
// LocalNotification.fireEvent(event, notification);
}
}
......@@ -30,16 +30,14 @@ import de.appplant.cordova.plugin.notification.Notification;
* Android notification bar. The notification uses the default notification
* sound and it vibrates the phone.
*/
public class TriggerReceiver extends de.appplant.cordova.plugin.notification.TriggerReceiver {
public class TriggerReceiver extends de.appplant.cordova.plugin.notification.receiver.TriggerReceiver {
/**
* 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 updated If an update has triggered or the original
*/
@Override
public void onTrigger (Notification notification, boolean updated) {
......@@ -53,13 +51,11 @@ public class TriggerReceiver extends de.appplant.cordova.plugin.notification.Tri
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
* @param builder Notification builder
*/
@Override
public Notification buildNotification (Builder builder) {
return builder
.setTriggerReceiver(TriggerReceiver.class)
.setClickActivity(ClickActivity.class)
.setClearReceiver(ClearReceiver.class)
.build();
......
......@@ -24,13 +24,13 @@ package de.appplant.cordova.plugin.notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import org.json.JSONObject;
import java.util.Random;
import de.appplant.cordova.plugin.notification.activity.ClickActivity;
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.
......@@ -43,9 +43,6 @@ public class Builder {
// Notification options passed by JS
private final Options options;
// Receiver to handle the trigger event
private Class<?> triggerReceiver;
// Receiver to handle the clear event
private Class<?> clearReceiver = ClearReceiver.class;
......@@ -55,21 +52,7 @@ public class Builder {
/**
* Constructor
*
* @param context
* Application context
* @param options
* Notification options
*/
public Builder(Context context, JSONObject options) {
this.context = context;
this.options = new Options(context).parse(options);
}
/**
* Constructor
*
* @param options
* Notification options
* @param options Notification options
*/
public Builder(Options options) {
this.context = options.getContext();
......@@ -77,21 +60,9 @@ public class Builder {
}
/**
* Set trigger receiver.
*
* @param receiver
* Broadcast receiver
*/
public Builder setTriggerReceiver(Class<?> receiver) {
this.triggerReceiver = receiver;
return this;
}
/**
* Set clear receiver.
*
* @param receiver
* Broadcast receiver
* @param receiver Broadcast receiver for the clear event.
*/
public Builder setClearReceiver(Class<?> receiver) {
this.clearReceiver = receiver;
......@@ -101,8 +72,7 @@ public class Builder {
/**
* Set click activity.
*
* @param activity
* Activity
* @param activity The activity to handler the click event.
*/
public Builder setClickActivity(Class<?> activity) {
this.clickActivity = activity;
......@@ -111,50 +81,45 @@ public class Builder {
/**
* Creates the notification with all its options passed through JS.
*
* @return The final notification to display.
*/
public Notification build() {
Uri sound = options.getSoundUri();
int smallIcon = options.getSmallIcon();
int ledColor = options.getLedColor();
NotificationCompat.Builder builder;
builder = new NotificationCompat.Builder(context)
.setDefaults(0)
builder = new NotificationCompat.Builder(context, "group")
.setDefaults(options.getDefaults())
.setContentTitle(options.getTitle())
.setContentText(options.getText())
.setNumber(options.getBadgeNumber())
.setTicker(options.getText())
.setAutoCancel(options.isAutoClear())
.setOngoing(options.isOngoing())
.setColor(options.getColor());
if (ledColor != 0) {
builder.setLights(ledColor, 100, 100);
}
if (sound != null) {
builder.setSound(sound);
}
if (smallIcon == 0) {
builder.setSmallIcon(options.getIcon());
.setOngoing(options.isSticky())
.setColor(options.getColor())
.setSound(options.getSound())
.setVisibility(options.getVisibility())
.setPriority(options.getPriority())
.setShowWhen(options.getShowWhen())
.setLights(options.getLedColor(), options.getLedOn(), options.getLedOff());
if (smallIcon != 0) {
builder.setSmallIcon(smallIcon);
builder.setLargeIcon(options.getLargeIcon());
} else {
builder.setSmallIcon(options.getSmallIcon());
builder.setLargeIcon(options.getIconBitmap());
builder.setSmallIcon(options.getIcon());
}
applyDeleteReceiver(builder);
applyContentReceiver(builder);
return new Notification(context, options, builder, triggerReceiver);
return new Notification(context, options, builder);
}
/**
* Set intent to handle the delete event. Will clean up some persisted
* preferences.
*
* @param builder
* Local notification builder instance
* @param builder Local notification builder instance
*/
private void applyDeleteReceiver(NotificationCompat.Builder builder) {
......@@ -162,7 +127,7 @@ public class Builder {
return;
Intent intent = new Intent(context, clearReceiver)
.setAction(options.getIdStr())
.setAction(options.getIdentifier())
.putExtra(Options.EXTRA, options.toString());
PendingIntent deleteIntent = PendingIntent.getBroadcast(
......@@ -175,8 +140,7 @@ public class Builder {
* Set intent to handle the click event. Will bring the app to
* foreground.
*
* @param builder
* Local notification builder instance
* @param builder Local notification builder instance
*/
private void applyContentReceiver(NotificationCompat.Builder builder) {
......
/*
* Copyright (c) 2014-2015 by appPlant UG. All rights reserved.
*
* @APPPLANT_LICENSE_HEADER_START@
*
* 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.
*
* @APPPLANT_LICENSE_HEADER_END@
*/
package de.appplant.cordova.plugin.notification;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import de.appplant.cordova.plugin.notification.trigger.MatchTrigger;
import de.appplant.cordova.plugin.notification.trigger.DateTrigger;
import de.appplant.cordova.plugin.notification.trigger.IntervalTrigger;
import static de.appplant.cordova.plugin.notification.trigger.IntervalTrigger.Unit;
public class Request {
// Key name for bundled extras
static final String EXTRA = "NOTIFICATION_OCCURRENCE_EXTRA";
// The options spec
private final Options options;
// The right trigger for the options
private final DateTrigger trigger;
// How often the trigger shall occur
private final int count;
// The trigger spec
private final JSONObject spec;
// The current trigger date
private Date triggerDate;
/**
* Constructor
*
* @param options The options spec.
*/
public Request(Options options) {
this.options = options;
this.spec = options.getTrigger();
this.count = spec.optInt("count", 1);
this.trigger = buildTrigger();
this.triggerDate = trigger.getNextTriggerDate(getBaseDate());
}
/**
* Gets the options spec.
*/
public Options getOptions() {
return options;
}
/**
* The identifier for the request.
*
* @return The notification ID as the string
*/
public String getIdentifier() {
return options.getId().toString() + "-" + getOccurrence();
}
/**
* The value of the internal occurrence counter.
*/
int getOccurrence() {
return trigger.getOccurrence();
}
/**
* If there's one more trigger date to calculate.
*/
private boolean hasNext() {
return triggerDate != null && getOccurrence() < count;
}
/**
* Moves the internal occurrence counter by one.
*/
boolean moveNext() {
if (hasNext()) {
triggerDate = getNextTriggerDate();
} else {
triggerDate = null;
}
return this.triggerDate != null;
}
/**
* Gets the current trigger date.
*
* @return null if there's no trigger date.
*/
Date getTriggerDate() {
Calendar now = Calendar.getInstance();
if (triggerDate == null)
return null;
if ((now.getTimeInMillis() - triggerDate.getTime()) > 60000) {
return null;
}
return triggerDate;
}
/**
* Gets the next trigger date based on the current trigger date.
*/
private Date getNextTriggerDate() {
return trigger.getNextTriggerDate(triggerDate);
}
/**
* Build the trigger specified in options.
*/
private DateTrigger buildTrigger() {
Object every = spec.opt("every");
if (every instanceof JSONObject) {
return new MatchTrigger(getDateMatchingComponents());
}
Unit unit = getUnit();
int ticks = getTicks();
return new IntervalTrigger(ticks, unit);
}
/**
* Gets the unit value.
*/
private Unit getUnit() {
Object every = spec.opt("every");
String unit = "SECOND";
if (spec.has("unit")) {
unit = spec.optString("unit", "second");
} else
if (every instanceof String) {
unit = spec.optString("every", "second");
}
return Unit.valueOf(unit.toUpperCase());
}
/**
* Gets the tick value.
*/
private int getTicks() {
Object every = spec.opt("every");
int ticks = 0;
if (spec.has("at")) {
ticks = 0;
} else
if (spec.has("in")) {
ticks = spec.optInt("in", 0);
} else
if (every instanceof String) {
ticks = 1;
} else
if (!(every instanceof JSONObject)) {
ticks = spec.optInt("every", 0);
}
return ticks;
}
/**
* Gets an array of all date parts to construct a datetime instance.
*
* @return [min, hour, day, month, year]
*/
private List<Integer> getDateMatchingComponents() {
JSONObject every = spec.optJSONObject("every");
return Arrays.asList(
(Integer) every.opt("minute"),
(Integer) every.opt("hour"),
(Integer) every.opt("day"),
(Integer) every.opt("month"),
(Integer) every.opt("year")
);
}
/**
* Gets the base date from where to calculate the next trigger date.
*/
private Date getBaseDate() {
if (spec.has("at")) {
return new Date(spec.optLong("at", 0) * 1000);
} else {
return new Date();
}
}
}
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.activity;
import android.app.Activity;
import android.content.Context;
......@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/**
* Abstract content receiver activity for local notifications. Creates the
* local notification and calls the event functions for further proceeding.
......@@ -38,8 +41,7 @@ abstract public class AbstractClickActivity extends Activity {
/**
* Called when local notification was clicked to launch the main intent.
*
* @param state
* Saved instance state
* @param state Saved instance state
*/
@Override
public void onCreate (Bundle state) {
......@@ -51,13 +53,11 @@ abstract public class AbstractClickActivity extends Activity {
try {
String data = bundle.getString(Options.EXTRA);
JSONObject options = new JSONObject(data);
Builder builder =
new Builder(context, options);
JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Notification notification =
buildNotification(builder);
new Notification(context, options);
onClick(notification);
} catch (JSONException e) {
......@@ -78,20 +78,11 @@ abstract public class AbstractClickActivity extends Activity {
/**
* Called when local notification was clicked by the user.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
abstract public void onClick (Notification notification);
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
abstract public Notification buildNotification (Builder builder);
/**
* Launch main intent from package.
*/
public void launchApp() {
......
......@@ -19,7 +19,9 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
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.
......@@ -32,28 +34,17 @@ public class ClickActivity extends AbstractClickActivity {
* Called when local notification was clicked by the user. Will
* move the app to foreground.
*
* @param notification
* Wrapper around the local notification
* @param notification Wrapper around the local notification
*/
@Override
public void onClick(Notification notification) {
launchApp();
if (notification.isRepeating()) {
notification.clear();
} else {
notification.cancel();
}
}
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
*/
public Notification buildNotification (Builder builder) {
return builder.build();
// if (notification.isRepeating()) {
// notification.clear();
// } else {
// notification.cancel();
// }
}
}
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
......@@ -29,6 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
import org.json.JSONObject;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/**
* Abstract delete receiver for local notifications. Creates the local
* notification and calls the event functions for further proceeding.
......@@ -38,35 +41,31 @@ abstract public class AbstractClearReceiver extends BroadcastReceiver {
/**
* Called when the notification was cleared from the notification center.
*
* @param context
* Application context
* @param intent
* Received intent with content data
* @param context Application context
* @param intent Received intent with content data
*/
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
JSONObject options;
String data = bundle.getString(Options.EXTRA);
try {
String data = bundle.getString(Options.EXTRA);
options = new JSONObject(data);
} catch (JSONException e) {
e.printStackTrace();
return;
}
JSONObject dict = new JSONObject(data);
Options options = new Options(context, dict);
Notification notification =
new Builder(context, options).build();
new Notification(context, options);
onClear(notification);
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 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
*/
abstract public void onClear (Notification notification);
......
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
......@@ -29,7 +29,9 @@ import android.os.Bundle;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Calendar;
import de.appplant.cordova.plugin.notification.Builder;
import de.appplant.cordova.plugin.notification.Notification;
import de.appplant.cordova.plugin.notification.Options;
/**
* Abstract broadcast receiver for local notifications. Creates the
......@@ -40,10 +42,8 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/**
* Called when an alarm was triggered.
*
* @param context
* Application context
* @param intent
* Received intent with content data
* @param context Application context
* @param intent Received intent with content data
*/
@Override
public void onReceive(Context context, Intent intent) {
......@@ -54,21 +54,15 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
String data = bundle.getString(Options.EXTRA);
JSONObject dict = new JSONObject(data);
options = new Options(context).parse(dict);
options = new Options(context, dict);
} catch (JSONException e) {
e.printStackTrace();
return;
}
if (options == null)
return;
if (isFirstAlarmInFuture(options))
return;
Builder builder = new Builder(options);
Notification notification = buildNotification(builder);
boolean updated = notification.isUpdate(false);
boolean updated = false;// notification.isUpdate(false);
onTrigger(notification, updated);
}
......@@ -76,45 +70,16 @@ abstract public class AbstractTriggerReceiver extends BroadcastReceiver {
/**
* 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 updated If an update has triggered or the original
*/
abstract public void onTrigger (Notification notification, boolean updated);
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
* @param builder Notification builder
*/
abstract public Notification buildNotification (Builder builder);
/*
* If you set a repeating alarm at 11:00 in the morning and it
* should trigger every morning at 08:00 o'clock, it will
* immediately fire. E.g. Android tries to make up for the
* 'forgotten' reminder for that day. Therefore we ignore the event
* if Android tries to 'catch up'.
*/
private Boolean isFirstAlarmInFuture (Options options) {
Notification notification = new Builder(options).build();
if (!notification.isRepeating())
return false;
Calendar now = Calendar.getInstance();
Calendar alarm = Calendar.getInstance();
alarm.setTime(notification.getOptions().getTriggerDate());
int alarmHour = alarm.get(Calendar.HOUR_OF_DAY);
int alarmMin = alarm.get(Calendar.MINUTE);
int currentHour = now.get(Calendar.HOUR_OF_DAY);
int currentMin = now.get(Calendar.MINUTE);
return (currentHour != alarmHour && currentMin != alarmMin);
}
}
......@@ -19,7 +19,9 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
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
......@@ -31,12 +33,11 @@ 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
*/
@Override
public void onClear (Notification notification) {
notification.clear();
// notification.clear();
}
}
......@@ -19,7 +19,10 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
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
......@@ -33,10 +36,8 @@ 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
* @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) {
......@@ -46,8 +47,7 @@ public class TriggerReceiver extends AbstractTriggerReceiver {
/**
* Build notification specified by options.
*
* @param builder
* Notification builder
* @param builder Notification builder
*/
@Override
public Notification buildNotification (Builder builder) {
......
/*
* 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.trigger;
import java.util.Calendar;
import java.util.Date;
abstract public class DateTrigger {
// Default unit is SECOND
public enum Unit { SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR }
// Internal counter
private int occurrence = 1;
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
abstract public Date getNextTriggerDate(Date base);
/**
* The value of the occurrence.
*/
public int getOccurrence() {
return occurrence;
}
/**
* Increase the occurrence by 1.
*/
void incOccurrence() {
occurrence += 1;
}
/**
* Gets a calendar instance pointing to the specified date.
*
* @param date The date to point.
*/
Calendar getCal (Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal;
}
}
\ 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.trigger;
import java.util.Calendar;
import java.util.Date;
/**
* Trigger class for interval based notification. Trigger by a fixed interval
* from now.
*/
public class IntervalTrigger extends DateTrigger {
// The number of ticks per interval
private final int ticks;
// The unit of the ticks
final Unit unit;
/**
* Interval trigger based from now.
*
* @param ticks The number of ticks per interval.
* @param unit The unit of the ticks.
*/
public IntervalTrigger(int ticks, Unit unit) {
this.ticks = ticks;
this.unit = unit;
}
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
@Override
public Date getNextTriggerDate(Date base) {
Calendar cal = getCal(base);
addInterval(cal);
incOccurrence();
return cal.getTime();
}
/**
* Adds the amount of ticks to the calendar.
*
* @param cal The calendar to manipulate.
*
* @return The calendar instance.
*/
Calendar addInterval(Calendar cal) {
switch (unit) {
case SECOND:
cal.add(Calendar.SECOND, ticks);
break;
case MINUTE:
cal.add(Calendar.MINUTE, ticks);
break;
case HOUR:
cal.add(Calendar.HOUR_OF_DAY, ticks);
break;
case DAY:
cal.add(Calendar.DAY_OF_YEAR, ticks);
break;
case WEEK:
cal.add(Calendar.WEEK_OF_YEAR, ticks);
break;
case MONTH:
cal.add(Calendar.MONTH, ticks);
break;
case QUARTER:
cal.add(Calendar.MONTH, ticks * 3);
break;
case YEAR:
cal.add(Calendar.YEAR, ticks);
break;
}
return cal;
}
}
\ 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.trigger;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.DAY;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.HOUR;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MINUTE;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MONTH;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.YEAR;
/**
* Trigger for date matching components.
*/
public class MatchTrigger extends IntervalTrigger {
// Used to determine the interval
private static Unit[] INTERVALS = { null, MINUTE, HOUR, DAY, MONTH, YEAR };
// The date matching components
private final List<Integer> matchers;
/**
* Date matching trigger from now.
*
* @param matchers Describes the date matching parts.
* { day: 15, month: ... }
*/
public MatchTrigger(List<Integer> matchers) {
super(1, INTERVALS[1 + matchers.indexOf(null)]);
this.matchers = matchers;
}
/**
* Gets the date from where to start calculating the initial trigger date.
*/
private Calendar getBaseTriggerDate(Date date) {
Calendar cal = getCal(date);
cal.set(Calendar.SECOND, 0);
if (matchers.get(0) != null) {
cal.set(Calendar.MINUTE, matchers.get(0));
} else {
cal.set(Calendar.MINUTE, 0);
}
if (matchers.get(1) != null) {
cal.set(Calendar.HOUR_OF_DAY, matchers.get(1));
} else {
cal.set(Calendar.HOUR_OF_DAY, 0);
}
if (matchers.get(2) != null) {
cal.set(Calendar.DAY_OF_MONTH, matchers.get(2));
}
if (matchers.get(3) != null) {
cal.set(Calendar.MONTH, matchers.get(3));
}
if (matchers.get(4) != null) {
cal.set(Calendar.YEAR, matchers.get(4));
}
return cal;
}
/**
* Gets the first trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none trigger date.
*/
private Date getTriggerDate (Date base) {
Calendar date = getBaseTriggerDate(base);
Calendar now = getCal(base);
if (date.compareTo(now) >= 0)
return date.getTime();
if (unit == null || date.get(Calendar.YEAR) < now.get(Calendar.YEAR))
return null;
if (date.get(Calendar.MONTH) < now.get(Calendar.MONTH)) {
switch (unit) {
case MINUTE:
case HOUR:
case DAY:
if (matchers.get(4) == null) {
return addToDate(date, now, Calendar.YEAR, 1);
} else break;
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.DAY_OF_YEAR) < now.get(Calendar.DAY_OF_YEAR)) {
switch (unit) {
case MINUTE:
case HOUR:
if (matchers.get(3) == null) {
return addToDate(date, now, Calendar.MONTH, 1);
} else
if (matchers.get(4) == null) {
return addToDate(date, now, Calendar.YEAR, 1);
}
else break;
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.HOUR_OF_DAY) < now.get(Calendar.HOUR_OF_DAY)) {
switch (unit) {
case MINUTE:
if (matchers.get(2) == null) {
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
} else
if (matchers.get(3) == null) {
return addToDate(date, now, Calendar.MONTH, 1);
}
else break;
case HOUR:
return addToDate(date, now, Calendar.HOUR_OF_DAY, 0);
case DAY:
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
} else
if (date.get(Calendar.MINUTE) < now.get(Calendar.MINUTE)) {
switch (unit) {
case MINUTE:
return addToDate(date, now, Calendar.MINUTE, 1);
case HOUR:
return addToDate(date, now, Calendar.HOUR_OF_DAY, 1);
case DAY:
return addToDate(date, now, Calendar.DAY_OF_YEAR, 1);
case MONTH:
return addToDate(date, now, Calendar.MONTH, 1);
case YEAR:
return addToDate(date, now, Calendar.YEAR, 1);
}
}
return null;
}
/**
* Gets the next trigger date.
*
* @param base The date from where to calculate the trigger date.
*
* @return null if there's none next trigger date.
*/
@Override
public Date getNextTriggerDate (Date base) {
Date date = base;
if (getOccurrence() > 1) {
Calendar cal = getCal(base);
addInterval(cal);
date = cal.getTime();
}
incOccurrence();
return getTriggerDate(date);
}
/**
* Sets the field value of now to date and adds by count.
*
* @return The new date.
*/
private Date addToDate(Calendar date, Calendar now, int field, int count) {
date.set(field, now.get(field));
date.add(field, count);
return date.getTime();
}
}
\ No newline at end of file
......@@ -19,7 +19,7 @@
* limitations under the License.
*/
package de.appplant.cordova.plugin.notification;
package de.appplant.cordova.plugin.notification.util;
import android.content.Context;
import android.content.res.AssetManager;
......@@ -42,20 +42,19 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.UUID;
import static android.media.RingtoneManager.TYPE_NOTIFICATION;
/**
* Util class to map unified asset URIs to native URIs. URIs like file:///
* map to absolute paths while file:// point relatively to the www folder
* within the asset resources. And res:// means a resource from the native
* res folder. Remote assets are accessible via http:// for example.
*/
class AssetUtil {
public class AssetUtil {
// Name of the storage folder
private static final String STORAGE_FOLDER = "/localnotification";
// Placeholder URI for default sound
private static final String DEFAULT_SOUND = "res://platform_default";
// Ref to the context passed through the constructor to access the
// resources and app directory.
private final Context context;
......@@ -63,8 +62,7 @@ class AssetUtil {
/**
* Constructor
*
* @param context
* Application context
* @param context Application context
*/
private AssetUtil(Context context) {
this.context = context;
......@@ -73,41 +71,22 @@ class AssetUtil {
/**
* Static method to retrieve class instance.
*
* @param context
* Application context
* @param context Application context
*/
static AssetUtil getInstance(Context context) {
public static AssetUtil getInstance(Context context) {
return new AssetUtil(context);
}
/**
* Parse path path to native URI.
*
* @param path
* Path to path file
*/
Uri parseSound (String path) {
if (path == null || path.isEmpty())
return Uri.EMPTY;
if (path.equalsIgnoreCase(DEFAULT_SOUND)) {
return RingtoneManager.getDefaultUri(RingtoneManager
.TYPE_NOTIFICATION);
}
return parse(path);
}
/**
* The URI for a path.
*
* @param path
* The given path
*/
Uri parse (String path) {
if (path.startsWith("res:")) {
public Uri parse (String path) {
if (path == null || path.isEmpty()) {
return Uri.EMPTY;
} else if (path.startsWith("res:")) {
return getUriForResourcePath(path);
} else if (path.startsWith("file:///")) {
return getUriFromPath(path);
......@@ -191,7 +170,7 @@ class AssetUtil {
*/
private Uri getUriForResourcePath(String path) {
String resPath = path.replaceFirst("res://", "");
int resId = getResIdForDrawable(resPath);
int resId = getResId(resPath);
File file = getTmpFile();
if (resId == 0) {
......@@ -296,14 +275,15 @@ class AssetUtil {
/**
* Resource ID for drawable.
*
* @param resPath
* Resource path as string
* @param resPath Resource path as string.
*
* @return The resource ID or 0 if not found.
*/
int getResIdForDrawable(String resPath) {
int resId = getResIdForDrawable(getPkgName(), resPath);
public int getResId(String resPath) {
int resId = getResId(context.getResources(), resPath);
if (resId == 0) {
resId = getResIdForDrawable("android", resPath);
resId = getResId(Resources.getSystem(), resPath);
}
return resId;
......@@ -312,56 +292,32 @@ class AssetUtil {
/**
* Resource ID for drawable.
*
* @param clsName
* Relative package or global android name space
* @param resPath
* Resource path as string
*/
int getResIdForDrawable(String clsName, String resPath) {
String drawable = getBaseName(resPath);
int resId = 0;
try {
Class<?> cls = Class.forName(clsName + ".R$drawable");
resId = (Integer) cls.getDeclaredField(drawable).get(Integer.class);
} catch (Exception ignore) {}
return resId;
}
/**
* Convert drawable resource to bitmap.
* @param res The resources where to look for.
* @param resPath The name of the resource.
*
* @param drawable
* Drawable resource name
* @return The resource ID or 0 if not found.
*/
Bitmap getIconFromDrawable (String drawable) {
Resources res = context.getResources();
int iconId;
iconId = getResIdForDrawable(getPkgName(), drawable);
private int getResId(Resources res, String resPath) {
String pkgName = getPkgName(res);
String resName = getBaseName(resPath);
int resId;
if (iconId == 0) {
iconId = getResIdForDrawable("android", drawable);
}
resId = res.getIdentifier(resName, "mipmap", pkgName);
if (iconId == 0) {
iconId = android.R.drawable.screen_background_dark_transparent;
if (resId == 0) {
resId = res.getIdentifier(resName, "drawable", pkgName);
}
return BitmapFactory.decodeResource(res, iconId);
return resId;
}
/**
* Convert URI to Bitmap.
*
* @param uri
* Internal image URI
* @param uri Internal image URI
*/
Bitmap getIconFromUri (Uri uri) throws IOException {
public Bitmap getIconFromUri(Uri uri) throws IOException {
InputStream input = context.getContentResolver().openInputStream(uri);
return BitmapFactory.decodeStream(input);
}
......@@ -422,10 +378,10 @@ class AssetUtil {
}
/**
* Package name specified by context.
* Package name specified by the resource bundle.
*/
private String getPkgName () {
return context.getPackageName();
private String getPkgName (Resources res) {
return res == Resources.getSystem() ? "android" : context.getPackageName();
}
}
......@@ -158,12 +158,13 @@
NSString* path = [dict objectForKey:@"sound"];
NSString* file;
if ([path isKindOfClass:NSNumber.class]) {
return [path boolValue] ? [UNNotificationSound defaultSound] : NULL;
}
if (!path.length)
return NULL;
if ([path isEqualToString:@"res://platform_default"])
return [UNNotificationSound defaultSound];
if ([path hasPrefix:@"file:/"]) {
file = [self soundNameForAsset:path];
} else
......
......@@ -51,12 +51,17 @@ exports.applyPlatformSpecificOptions = function () {
switch (device.platform) {
case 'Android':
defaults.icon = 'res://ic_popup_reminder';
defaults.icon = 'res://icon';
defaults.smallIcon = undefined;
defaults.ongoing = false;
defaults.sticky = false;
defaults.autoClear = true;
defaults.led = undefined;
defaults.led = true;
defaults.color = undefined;
defaults.vibrate = false;
defaults.lockscreen = true;
defaults.showWhen = true;
defaults.priority = 0;
defaults.defaults = 0;
break;
}
};
......@@ -111,14 +116,17 @@ exports.mergeWithDefaults = function (options) {
* @return [ Object ] The converted property list
*/
exports.convertProperties = function (options) {
if (options.id) {
if (isNaN(options.id)) {
options.id = this.getDefaults().id;
console.warn('Id is not a number: ' + options.id);
var parseToInt = function (prop, options) {
if (isNaN(options[prop])) {
console.warn(prop + ' is not a number: ' + options[prop]);
return this.getDefaults()[prop];
} else {
options.id = Number(options.id);
return Number(options[prop]);
}
};
if (options.id) {
options.id = parseToInt('id', options);
}
if (options.title) {
......@@ -130,12 +138,15 @@ exports.convertProperties = function (options) {
}
if (options.badge) {
if (isNaN(options.badge)) {
options.badge = this.getDefaults().badge;
console.warn('Badge number is not a number: ' + options.id);
} else {
options.badge = Number(options.badge);
options.badge = parseToInt('badge', options);
}
if (options.priority) {
options.priority = parseToInt('priority', options);
}
if (options.defaults) {
options.defaults = parseToInt('defaults', options);
}
if (typeof options.data == 'object') {
......
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