Skip to content

Commit

Permalink
Merge pull request #2042 from GitFr33/configurable-channels
Browse files Browse the repository at this point in the history
Configurable Android notification channels
  • Loading branch information
GitToTheHub authored Oct 4, 2024
2 parents 0c93296 + c929020 commit 4c40999
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 11 deletions.
65 changes: 54 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 | | | |
Expand All @@ -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) | - | <img src="images/android-icon.svg" width="16"> Android only. Currently not supported. Notification channels have to be integrated first. |
| vibrate | (x) | | <img src="images/android-icon.svg" width="16"> Android only. Since Android 8 it must be set with channel. |
| wakeup | | | |

For their default values see:
Expand Down Expand Up @@ -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].
Expand Down
18 changes: 18 additions & 0 deletions src/android/LocalNotification.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
}

/**
Expand Down
50 changes: 50 additions & 0 deletions src/android/notification/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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.
*
Expand Down
26 changes: 26 additions & 0 deletions www/local-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down

0 comments on commit 4c40999

Please sign in to comment.