diff --git a/README.md b/README.md
index ce1168818..2166aad87 100644
--- a/README.md
+++ b/README.md
@@ -51,12 +51,6 @@ Please make sure that you always read the tagged README for the version you're u
Please report bugs or missing features.
-## Lacking features
-
-### Android
-
-- Channels are not configurable. A default channel will be created with [IMPORTANCE_DEFAULT](https://developer.android.com/reference/android/app/NotificationManager#IMPORTANCE_DEFAULT), the id `default-channel-id` and the name `Default channel`. The name ist not translatable.
-
## Basics
The plugin creates the object `cordova.plugins.notification.local` and is accessible after *deviceready* has been fired.
@@ -97,7 +91,7 @@ A notification does have a set of configurable properties.
| attachments | | | |
| autoClear | | | |
| badge | | | |
-| channel | x | - | Don't use it, it is not functional. Since Android 8: Channel-ID of the chanel. In the future it will be renamed to channelId |
+| channel | | - | Channel-ID of the channel (see: createChannel). In the future it will be renamed to channelId |
| clock | | | |
| color | | | |
| data | | | |
@@ -118,14 +112,14 @@ A notification does have a set of configurable properties.
| progressBar | x | - | Natively not supported by iOS, [see Stackoverflow](https://stackoverflow.com/questions/48500532/progress-view-in-local-notification/48500734#48500734) |
| silent | | | |
| smallIcon | | | |
-| sound | (x) | (x) | Property available but not useable. Cannot used to play a sound file. In Android it's only possible up to Android 7.1. Since Android 8, the channels take precedence and a channel would have to be created with a sound file, but this is not implemented yet. In iOS it would be possible, but must be implemented too. |
+| sound | (x) | (x) | Property available but not useable. In Android it may work up to Android 7.1. Since Android 8 it must be set with createChannel. In iOS it would be possible, but must be implemented too. |
| sticky | | | |
| summary | | | |
| text | x | x | Text of the notification. For Android exists some special features: 1. It can be a JSONArray to [summarize](#summarizing) notifications. [NotificationCompat.MessagingStyle](https://developer.android.com/reference/androidx/core/app/NotificationCompat.MessagingStyle) will then be used. Using an JSONArray for iOS would result in a crash. 2. If the text contains line breaks (`\n`) the notification style [NotificationCompat.InboxStyle](https://developer.android.com/reference/androidx/core/app/NotificationCompat.InboxStyle) would be used. 3. If the text is longer then 44 chars, the notifications style [NotificationCompat.BigTextStyle](https://developer.android.com/reference/androidx/core/app/NotificationCompat.BigTextStyle) will be used. |
| timeoutAfter | | | |
| title | | | |
| trigger | | | |
-| vibrate | (x) | - | Android only. Currently not supported. Notification channels have to be integrated first. |
+| vibrate | (x) | | Android only. Since Android 8 it must be set with channel. |
| wakeup | | | |
For their default values see:
@@ -523,11 +517,60 @@ See the sample app for how to use them.
| :------- | :---------------- | :-------------- | :------------- | :------------ | :--------------- |
| schedule | cancelAll | isTriggered | get | removeActions | un |
| update | hasPermission | getType | getAll | hasActions | fireQueuedEvents |
-| clear | requestPermission | getIds | getScheduled | getDefaults |
-| clearAll | isPresent | getScheduledIds | getTriggered | setDefaults |
+| clear | requestPermission | getIds | getScheduled | getDefaults | createChannel |
+| clearAll | isPresent | getScheduledIds | getTriggered | setDefaults | canScheduleExactAlarms|
| cancel | isScheduled | getTriggeredIds | addActions | on |
+
+## Android Notification Channels
+
+On Android 8 and above a Notification Channel is required and is the only way to set a non-default sound, vibration or importance.
+
+A default channel is automatically created with [IMPORTANCE_DEFAULT](https://developer.android.com/reference/android/app/NotificationManager#IMPORTANCE_DEFAULT), the id `default-channel-id` and the name `Default channel`. (The name is not translatable.) Any notification that doesn't specify a `channel` will use this default channel with default sound and vibration settings.
+
+To customize vibration, sound, or importance, create a channel as follows:
+
+```js
+cordova.plugins.notification.local.createChannel({
+ channelId:'my_ch_id', // string
+ channelName:'My Channel Name', // string
+ description:"Description of channel", // string (optional)
+ sound: 'file://audio/ring.mp3', // string (optional)
+ vibrate: true, // bool (optional)
+ importance: 3, // int (optional) 0 to 4
+ soundUsage: 5, // int (optional)
+ }, success_callback)
+```
+
+**Options for `importance`** [Documentation](https://developer.android.com/reference/android/app/NotificationManager#IMPORTANCE_DEFAULT)
+0 IMPORTANCE_NONE
+1 IMPORTANCE_MIN
+2 IMPORTANCE_LOW
+3 IMPORTANCE_DEFAULT
+4 IMPORTANCE_HIGH
+
+**Options for `soundUsage`** [Documentation](https://developer.android.com/reference/android/media/AudioAttributes.Builder#setUsage(int))
+0: USAGE_UNKNOWN
+1: USAGE_MEDIA
+2: USAGE_VOICE_COMMUNICATION
+3: USAGE_VOICE_COMMUNICATION_SIGNALLING
+4: USAGE_ALARM
+5: USAGE_NOTIFICATION
+6: USAGE_NOTIFICATION_RINGTONE
+7: USAGE_NOTIFICATION_COMMUNICATION_REQUEST
+8: USAGE_NOTIFICATION_COMMUNICATION_INSTANT
+9: USAGE_NOTIFICATION_COMMUNICATION_DELAYED
+10: USAGE_NOTIFICATION_EVENT
+11: USAGE_ASSISTANCE_ACCESSIBILITY
+12: USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
+13: USAGE_ASSISTANCE_SONIFICATION
+14: USAGE_GAME
+16: USAGE_ASSISTANT
+
+Then use the `channelId` value as the `channel` when scheduling a notification. It is not possible to programmatically update a channel once it has been created.
+
+
## Installation
The plugin can be installed via [Cordova-CLI][CLI] and is publicly available on [NPM][npm].
diff --git a/src/android/LocalNotification.java b/src/android/LocalNotification.java
index a49dea629..ac102d504 100644
--- a/src/android/LocalNotification.java
+++ b/src/android/LocalNotification.java
@@ -156,6 +156,9 @@ public boolean execute (final String action, final JSONArray args,
public void run() {
if (action.equals("ready")) {
deviceready();
+ } else
+ if (action.equals("createChannel")) {
+ createChannel(args, command);
} else
if (action.equals("check")) {
check(command);
@@ -334,6 +337,21 @@ private void schedule (JSONArray toasts, CallbackContext command) {
}
check(command);
+ }
+
+ /**
+ * Create Notification channel with options.
+ *
+ * @param args The channel options.
+ * @param command The callback context used when calling back into
+ * JavaScript.
+ */
+ private void createChannel (JSONArray args, CallbackContext command) {
+ Manager mgr = getNotMgr();
+ JSONObject options = args.optJSONObject(0);
+ mgr.createChannel(options);
+
+ command.success();
}
/**
diff --git a/src/android/notification/Manager.java b/src/android/notification/Manager.java
index ebe350ed0..9375dcfd2 100644
--- a/src/android/notification/Manager.java
+++ b/src/android/notification/Manager.java
@@ -31,6 +31,9 @@
import android.service.notification.StatusBarNotification;
import androidx.core.app.NotificationManagerCompat;
import android.app.AlarmManager;
+import android.media.AudioAttributes;
+import android.net.Uri;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -48,6 +51,7 @@
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT;
import static de.appplant.cordova.plugin.notification.Notification.PREF_KEY_ID;
import static de.appplant.cordova.plugin.notification.Notification.Type.TRIGGERED;
+import de.appplant.cordova.plugin.notification.util.AssetUtil;
/**
* Central way to access all or single local notifications set by specific
@@ -140,6 +144,52 @@ private void createDefaultChannel() {
mgr.createNotificationChannel(channel);
}
+ /**
+ * Create Notification channel with options
+ * @param options Set of channel options.
+ *
+ */
+ public void createChannel(JSONObject options) {
+ NotificationManager mgr = getNotMgr();
+
+ if (SDK_INT < O)
+ return;
+
+ String channelId = options.optString("channelId", "");
+ CharSequence channelName = options.optString("channelName", "");
+ int importance = options.optInt("importance",IMPORTANCE_DEFAULT);
+ NotificationChannel channel = mgr.getNotificationChannel(channelId);
+
+ if (channel != null)
+ return;
+
+ channel = new NotificationChannel(channelId, channelName, importance);
+
+ if(options.has("vibrate")){
+ Boolean shouldVibrate = options.optBoolean("vibrate", false);
+ channel.enableVibration(shouldVibrate);
+ }
+
+ if(options.has("sound")){
+ AssetUtil assets = AssetUtil.getInstance(this.context);
+ Uri soundUri = assets.parse(options.optString("sound", null));
+
+ if (!soundUri.equals(Uri.EMPTY)) {
+ AudioAttributes attributes = new AudioAttributes.Builder().setUsage(options.optInt("soundUsage",AudioAttributes.USAGE_NOTIFICATION)).build();
+ channel.setSound(soundUri, attributes);
+ }else{
+ channel.setSound(null, null);
+ }
+ }
+
+ if(options.has("description")){
+ channel.setDescription(options.optString("description", " "));
+ }
+
+ mgr.createNotificationChannel(channel);
+ }
+
+
/**
* Update local notification specified by ID.
*
diff --git a/www/local-notification.js b/www/local-notification.js
index 4f2cf87a0..2b77e88fa 100644
--- a/www/local-notification.js
+++ b/www/local-notification.js
@@ -63,6 +63,32 @@ exports._defaults = {
// Event listener
exports._listener = {};
+/**
+ * Create Notification Channel. Android only.
+ *
+ * @param [ Object ] options channel config options.
+ * @param [ Function ] callback The function to be exec as the callback.
+ * @param [ Object ] scope The callback function's scope.
+ *
+ * @return [ Void ]
+ */
+exports.createChannel = function (options, callback, scope) {
+
+ if(typeof(options.channelId) != "string"){
+ console.error('createChannel error: channelId must be a string.');
+ return false;
+ }
+
+ if(typeof(options.channelName) != "string"){
+ console.error('createChannel error: channelName must be a string.');
+ return false;
+ }
+
+ var toasts = this._toArray(options);
+
+ this._exec('createChannel', toasts, callback, scope);
+};
+
/**
* Check permission to show notifications.
*