A Capacitor plugin that allows continuous background work by using a persistent notification in Android.
A Capacitor plugin that allows a continuous background service by using a persistent notification in Android. Your background task/service is written in JavaScript, no need for platform specific code (except that required of enabling a Capacitor plugin.)
Based upon, though not a fork of, the Cordova Background Mode Plugin.
In order to allow an app written in HTML5/Javascript/CSS to continuously run in the background using a persistent foreground service notification in Android.
This plugin uses the new Ionic/Capacitor plugin system
Unfortunately, because of system limitations, this plugin ONLY works on Android! The plugin calls are NO-OP on the web platform, and non-existent in iOS and Electron. Further research may allow a persistent background service in Electron. iOS does not have a system that allows a persistent background service. (I understand there are hacky methods to make it work in iOS, but until there is a proper API I plan no updates for iOS.)
KNOWN ISSUE LIMITATION: Unfortunately because the JavaScript code used to run a Capacitor app lives in an Android Activity, it is not currently possible to restart a service upon shutdown, crash, or reboot. See Android limitation. I am currently researching possible solutions. For now try to keep memory consumption to a minimum while app is in “background” state.
capacitor-persistent-notification
Ensure Android is added to your Capacitor based project:
npx cap add android
Use NPM in your project directory to install the plugin.
npm install capacitor-persistent-notification@latest --save
npx cap update
Ensure your project includes the Capacitor plugin code.
Standard HTML import:
<script src="capacitor.js"></script>
const { Plugins } = Capacitor;
const { PersistentNotification } = Plugins;
or Node Imports:
import { Plugins } from '@capacitor/core';
const { PersistentNotification } = Plugins;
Be sure to add the plugin to your MainActivity in your Android project:
import com.flytesoft.persistent.notification.PersistentNotification;
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initializes the Bridge
this.init(savedInstanceState, new ArrayList<Class<? extends Plugin>>() {
{
// Additional plugins you've installed go here
// Ex: add(TotallyAwesomePlugin.class);
add(PersistentNotification.class);
}
});
}
}
Add the foreground service permission to the AndroidManifest.xml:
<!-- Permissions -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" ></uses-permission>
It is recommended, although not required, to open your notification upon your app closing:
const { PersistentNotification, App, BackgroundTask } = Plugins;
...
let listener = null;
App.addListener('appStateChange', (state) => {
// Listen for user clicks on the notification.
// OK to listen before opening.
listener = constPersistentNotification.addListener('notificationclick', ({ action } => {
console.log("Persistent notification click: ", action);
if(!action) // A button was NOT clicked.
{
// Put the app in the foreground
// and close the notification, if desired.
PersistentNotification.appToForeground();
PersistentNotification.close();
}
else // A button was clicked by the user.
{
if(action === 'button-click2')
{
console.log("Button 2 was clicked!");
}
}
});
if (!state.isActive) // App has gone inactive or closed
{
// Get some work done before the app closes completely.
const taskId = BackgroundTask.beforeExit(async () => {
try
{
await PersistentNotification.open({
title: "Background Forever!",
icon: "icons/icon.png",
// Icon asset exist in www/icons/icon.png
// Icon asset always based upon TLD and
// NOT the location of your code.
body: "We can run continuously!",
actions: [{
title: "button",
action: "button-click",
icon: "icons/icon.png"
},
{
title: "button2",
action: "button-click2",
icon: "icons/icon.png"
}]
});
// See if the notification is open.
const { isOpen } = await PersistentNotification.getState();
console.log("Is open: ", isOpen);
}
catch(e)
{
console.log("Unable to start background service: ", e);
}
// Let the app close.
BackgroundTask.finish({
taskId
});
});
/**
* It is recommended you stop any code that updates the DOM:
* The DOM will still be 'awake' but not visible to the user.
* So save CPU power.
*
* stopVisualTasks();
* */
// Now do your continuous background task.
// Update the notification as necessary.
let interval = 1;
setInterval(() => {
PersistentNotification.update({
body: `Seconds gone by: ${interval}`);
interval++;
}, 1000);
}
else // App is now opening or resuming.
{
// OK to close un-opened notification.
PersistentNotification.close().
catch(e => {
console.log("Trouble closing the persistent notification: ", e);
});
// remove the listener.
if(listener != null)
{
listener.remove();
listener = null;
}
}
});
The API is similar to the the standard ES6+ Notification API. However, the PersistentNotification class is completely static and all methods return Promises, as do most of the Capacitor APIs and plugins.
Promise
A method to open and configure your persistent notification. Returns success upon notification opening. Configuration options are only optional if you have previously called the update method.
Kind: Static instance method of PersistentNotification
Category: async
Fulfil: undefined
Throws: Error
| Param | Type | Description |
| —- | —- | —- |
| [options] | object
| Similar to the Notification API options. |
| [options.title] | string
| Set the title of the notification. Limited HTML may be used (Required) |
| [options.body] | string
| Set the content or body area of the notification. Limited HTML may be used (Required)|
| [options.color] | string
| Set the highlight color of the notification. Hex code or color names only). If undefined or invalid, defaults to blue. |
| [options.actions] | Array.<NotificationAction>
| An array of one or more buttons to be included. |
| [options.icon] | string
| Location of the icon to be displayed in the status bar for the notification. Must use a relative path to icon resource from your top level directory. If undefined or invalid, a default icon is provided. |
string
| Location of a large (badge type) icon to be displayed in the notification. Must use a relative path to the resource from your top level directory. If undefined or invalid, no badge is displayed. |Promise
A method to configure and/or update a current notification. If a notification is not already open your configuration will be maintained until open is called. Only update options you need to update. See open for parameters.
Kind: Static instance method of PersistentNotification
Category: async
Fulfil: undefined
Throws: Error
Promise
Closes the notification. If the notification is not open, the method is NO-OP and returns success. If unable to close the notification an error is thrown.
Kind: Static instance method of PersistentNotification
Category: async
Fulfil: undefined
Throws: Error
Promise
Brings the main application view or webview into the foreground. If the app is already in the foreground the method is NO-OP. Useful to call when the user has clicked on the notification.
Kind: Static instance method of PersistentNotification
Category: async
Fulfil: undefined
Promise
A promise that returns whether the notification is currently open.
Kind: Static instance method of PersistentNotification
Category: async
Fulfil: state
ListenerHandle
Kind: Static instance method of PersistentNotification
Category: EventListener
Returns: ListenerHandle
Add an event listener when the notification is clicked. The data object is passed to the listener function.
| Param | Type | Description |
| —- | —- | —- |
| data | object
| Data object passed to event listener function |
| data.action | string
| Actions string value, if a button is clicked the value will be the ‘action’ or title of that button. An empty string indicates the notification was clicked by the user. |
object
Category: object
Object containing the event listener for the notification. Call remove() to delete the event listener.
Param | Type | Description |
---|---|---|
remove | function |
Remove the event listener. |
object
Category: object
Object containing the options for a button to be displayed in the notification, similar to the NotificationAction object in ES6+.
Param | Type | Description |
---|---|---|
[title] | string |
The title of the button (required). |
[action] | string |
The action data of the button, will be returned upon ‘notificationclick’ event. If undefined, the title will be used. |
[icon] | string |
Location of the icon to be displayed in the button. Must use a relative path to icon resource from your top level directory. If undefined or invalid, a default icon is provided. The icon is NOT displayed in Android 9.0+. |
object
Category: object
Object containing the state information of the notification
Param | Type | Description |
---|---|---|
[isOpen] | boolean |
Whether the notification is open or not. |
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
If you found this project useful and you would like to help buy me a cup of coffee, consider a donation via Paypal. (Not tax deductible. Non-charitable.) Either way, I am hoping you find this project useful.