Skip to content

Commit

Permalink
Merge pull request #38 from voximplant/foreground_notification_button
Browse files Browse the repository at this point in the history
Button on foreground notification
  • Loading branch information
pe1ros authored Mar 9, 2022
2 parents ac2636c + ed76244 commit 2743dc9
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 10 deletions.
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ async startForegroundService() {
id: 3456,
title: 'Title',
text: 'Some text',
icon: 'ic_icon'
icon: 'ic_icon',
button: 'Some text',
};
try {
await VIForegroundService.startService(notificationConfig);
Expand All @@ -112,25 +113,41 @@ VIForegroundService.stopService();

### Methods
```javascript
static async startService(notificationConfig)
async startService(notificationConfig)
```
Starts the foreground service and displays a notification with the defined configuration

------------------------------

```javascript
static async stopService()
async stopService()
```
Stops the foreground service

------------------------------

```javascript
static async createNotificationChannel(channelConfig)
async createNotificationChannel(channelConfig)
```
Creates a notification channel for the foreground service.
For Android 8+ the notification channel should be created before starting the foreground service

------------------------------

```javascript
on(event, handler)
```
Adds a `handler` to be invoked when a button on the notification is pressed.
Supported event: `VIForegroundServiceButtonPressed`.

------------------------------

```javascript
off(event, handler)
```
Removes the registered `handler` for the `VIForegroundServiceButtonPressed` event.
If `handler` is not provided, this function will remove all registered handlers.

### Configs
```javascript
NotificationChannelConfig
Expand All @@ -154,4 +171,5 @@ NotificationConfig
| title | Notification title | yes |
| text | Notification text | yes |
| icon | Icon name | yes |
| button | Button text | no |
| priority | Priority of this notification. One of: <ul><li>&nbsp;0 – PRIORITY_DEFAULT (by default)</li><li>-1 – PRIORITY_LOW</li><li>-2 – PRIORITY_MIN</li><li>&nbsp;1 – PRIORITY_HIGH</li><li>&nbsp;2 – PRIORITY_MAX</li></ul> | no |
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ class Constants {
static final String ERROR_SERVICE_ERROR = "ERROR_SERVICE_ERROR";
static final String ERROR_ANDROID_VERSION = "ERROR_ANDROID_VERSION";

static final String FOREGROUND_SERVICE_BUTTON_PRESSED = "FOREGROUND_SERVICE_BUTTON_PRESSED";
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReadableMap;

import static com.voximplant.foregroundservice.Constants.FOREGROUND_SERVICE_BUTTON_PRESSED;
import static com.voximplant.foregroundservice.Constants.ERROR_ANDROID_VERSION;
import static com.voximplant.foregroundservice.Constants.ERROR_INVALID_CONFIG;

Expand Down Expand Up @@ -131,6 +132,14 @@ Notification buildNotification(Context context, Bundle notificationConfig) {
notificationBuilder.setSmallIcon(getResourceIdForResourceName(context, iconName));
}

if (notificationConfig.containsKey("button")) {
Intent buttonIntent = new Intent();
buttonIntent.setAction(FOREGROUND_SERVICE_BUTTON_PRESSED);
PendingIntent pendingButtonIntent = PendingIntent.getBroadcast(context, 0, buttonIntent, 0);

notificationBuilder.addAction(0, notificationConfig.getString("button"), pendingButtonIntent);
}

return notificationBuilder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,57 @@

package com.voximplant.foregroundservice;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

import static com.voximplant.foregroundservice.Constants.ERROR_INVALID_CONFIG;
import static com.voximplant.foregroundservice.Constants.ERROR_SERVICE_ERROR;
import static com.voximplant.foregroundservice.Constants.NOTIFICATION_CONFIG;
import static com.voximplant.foregroundservice.Constants.FOREGROUND_SERVICE_BUTTON_PRESSED;



public class VIForegroundServiceModule extends ReactContextBaseJavaModule {

class ForegroundReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
buttonPressedEvent();
}
}

private final ReactApplicationContext reactContext;
private ForegroundReceiver foregroundReceiver = new ForegroundReceiver();

public VIForegroundServiceModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}

@ReactMethod
public void addListener(String eventName) {
// Set up any upstream listeners or background tasks as necessary
}

@ReactMethod
public void removeListeners(Integer count) {
// Remove upstream listeners, stop unnecessary background tasks
}

@Override
public String getName() {
return "VIForegroundService";
Expand Down Expand Up @@ -80,6 +107,11 @@ public void startService(ReadableMap notificationConfig, Promise promise) {
intent.setAction(Constants.ACTION_FOREGROUND_SERVICE_START);
intent.putExtra(NOTIFICATION_CONFIG, Arguments.toBundle(notificationConfig));
ComponentName componentName = getReactApplicationContext().startService(intent);

IntentFilter filter = new IntentFilter();
filter.addAction(FOREGROUND_SERVICE_BUTTON_PRESSED);
getReactApplicationContext().registerReceiver(foregroundReceiver, filter);

if (componentName != null) {
promise.resolve(null);
} else {
Expand All @@ -98,4 +130,16 @@ public void stopService(Promise promise) {
promise.reject(ERROR_SERVICE_ERROR, "VIForegroundService: Foreground service failed to stop");
}
}

public void buttonPressedEvent() {
WritableMap params = Arguments.createMap();
params.putString("event", FOREGROUND_SERVICE_BUTTON_PRESSED);
sendEvent("VIForegroundServiceButtonPressed", params);
}

private void sendEvent(String eventName, @Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
}
88 changes: 82 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

'use strict';

import { NativeModules } from 'react-native';
import { NativeModules, NativeEventEmitter } from 'react-native';

const ForegroundServiceModule = NativeModules.VIForegroundService;
const EventEmitter = new NativeEventEmitter(ForegroundServiceModule);

/**
* @property {string} channelId - Notification channel id to display notification
Expand All @@ -20,6 +21,7 @@ const ForegroundServiceModule = NativeModules.VIForegroundService;
* -2 - PRIORITY_MIN,
* 1 - PRIORITY_HIGH,
* 2- PRIORITY_MAX
* @property {string} button - If this property exist, notification will be contain button with text as button value
*/
const NotificationConfig = {

Expand All @@ -41,14 +43,32 @@ const NotificationChannelConfig = {

};

export default class VIForegroundService {

class VIForegroundService {
static _serviceInstance = null;
_listeners = new Map();

/**
* @private
*/
constructor() {
EventEmitter.addListener('VIForegroundServiceButtonPressed', this._VIForegroundServiceButtonPressed.bind(this));
}

static getInstance() {
if (this._serviceInstance === null) {
this._serviceInstance = new VIForegroundService();
}
return this._serviceInstance;
}

/**
* Create notification channel for foreground service
*
* @param {NotificationChannelConfig} channelConfig - Notification channel configuration
* @return Promise
*/
static async createNotificationChannel(channelConfig) {
async createNotificationChannel(channelConfig) {
return await ForegroundServiceModule.createNotificationChannel(channelConfig);
}

Expand All @@ -57,7 +77,7 @@ export default class VIForegroundService {
* @param {NotificationConfig} notificationConfig - Notification config
* @return Promise
*/
static async startService(notificationConfig) {
async startService(notificationConfig) {
return await ForegroundServiceModule.startService(notificationConfig);
}

Expand All @@ -66,9 +86,65 @@ export default class VIForegroundService {
*
* @return Promise
*/
static async stopService() {
async stopService() {
return await ForegroundServiceModule.stopService();
}
}

/**
* Adds a handler to be invoked when button on notification will be pressed.
* The data arguments emitted will be passed to the handler function.
*
* @param event - Name of the event to listen to
* @param handler - Function to invoke when the specified event is emitted
*/
on(event, handler) {
if (!handler || !(handler instanceof Function)) {
console.warn(`ForegroundService: on: handler is not a Function`);
return;
}
if (!this._listeners.has(event)) {
this._listeners.set(event, new Set());
}
this._listeners.get(event)?.add(handler);
}

/**
* Removes the registered `handler` for the specified event.
*
* If `handler` is not provided, this function will remove all registered handlers.
*
* @param event - Name of the event to stop to listen to.
* @param handler - Handler function.
*/
off(event, handler) {
if (!this._listeners.has(event)) {
return;
}
if (handler && handler instanceof Function) {
this._listeners.get(event)?.delete(handler);
} else {
this._listeners.set(event, new Set());
}
}

/**
* @private
*/
_emit(event, ...args) {
const handlers = this._listeners.get(event);
if (handlers) {
handlers.forEach((handler) => handler(...args));
} else {
console.log(`[VIForegroundService]: _emit: no handlers for event: ${eventType}`);
}
}

/**
* @private
*/
_VIForegroundServiceButtonPressed(event) {
this._emit('VIForegroundServiceButtonPressed', event);
}
}

export default VIForegroundService;

0 comments on commit 2743dc9

Please sign in to comment.