Skip to content

Commit

Permalink
feat(android): Add notification action handling and listener methods
Browse files Browse the repository at this point in the history
  • Loading branch information
hahagu committed Jan 24, 2025
1 parent 85bb2ba commit 6fde8cc
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 26 deletions.
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ Visit [Don't kill my app!](https://dontkillmyapp.com) for more information on th

## Limitations of Background Tasks

Its not possible to run persistent, always running background services on mobile operating systems. Due to the limitations imposed by iOS and Android designed to reduce battery and data consumption, background tasks are constrained with various limitations that you must keep in mind while designing and implementing your background task.
It's not possible to run persistent, always running background services on mobile operating systems. Due to the limitations imposed by iOS and Android designed to reduce battery and data consumption, background tasks are constrained with various limitations that you must keep in mind while designing and implementing your background task.

### iOS

Expand All @@ -289,6 +289,8 @@ It’s not possible to run persistent, always running background services on mob
* [`checkPermissions()`](#checkpermissions)
* [`requestPermissions(...)`](#requestpermissions)
* [`dispatchEvent(...)`](#dispatchevent)
* [`addListener('backgroundRunnerNotificationReceived', ...)`](#addlistenerbackgroundrunnernotificationreceived-)
* [`removeNotificationListeners()`](#removenotificationlisteners)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)

Expand Down Expand Up @@ -350,6 +352,41 @@ Dispatches an event to the configured runner.
--------------------


### addListener('backgroundRunnerNotificationReceived', ...)

```typescript
addListener(eventName: 'backgroundRunnerNotificationReceived', listenerFunc: (event: NotificationActionEvent) => void) => any
```

Add a listener for notification actions.

| Param | Type |
| ------------------ | ----------------------------------------------------------------------------------------------- |
| **`eventName`** | <code>'backgroundRunnerNotificationReceived'</code> |
| **`listenerFunc`** | <code>(event: <a href="#notificationactionevent">NotificationActionEvent</a>) =&gt; void</code> |

**Returns:** <code>any</code>

**Since:** 2.1.1

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


### removeNotificationListeners()

```typescript
removeNotificationListeners() => any
```

Remove notification action listeners for this plugin.

**Returns:** <code>any</code>

**Since:** 2.1.1

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


### Interfaces


Expand Down Expand Up @@ -377,6 +414,21 @@ Dispatches an event to the configured runner.
| **`details`** | <code>{ [key: string]: any; }</code> | | |


#### NotificationActionEvent

| Prop | Type |
| -------------------- | ------------------- |
| **`actionTypeId`** | <code>string</code> |
| **`notificationId`** | <code>number</code> |


#### PluginListenerHandle

| Prop | Type |
| ------------ | ------------------------- |
| **`remove`** | <code>() =&gt; any</code> |


### Type Aliases


Expand Down
54 changes: 53 additions & 1 deletion packages/capacitor-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ Visit [Don't kill my app!](https://dontkillmyapp.com) for more information on th

## Limitations of Background Tasks

Its not possible to run persistent, always running background services on mobile operating systems. Due to the limitations imposed by iOS and Android designed to reduce battery and data consumption, background tasks are constrained with various limitations that you must keep in mind while designing and implementing your background task.
It's not possible to run persistent, always running background services on mobile operating systems. Due to the limitations imposed by iOS and Android designed to reduce battery and data consumption, background tasks are constrained with various limitations that you must keep in mind while designing and implementing your background task.

### iOS

Expand All @@ -289,6 +289,8 @@ It’s not possible to run persistent, always running background services on mob
* [`checkPermissions()`](#checkpermissions)
* [`requestPermissions(...)`](#requestpermissions)
* [`dispatchEvent(...)`](#dispatchevent)
* [`addListener('backgroundRunnerNotificationReceived', ...)`](#addlistenerbackgroundrunnernotificationreceived-)
* [`removeNotificationListeners()`](#removenotificationlisteners)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)

Expand Down Expand Up @@ -350,6 +352,41 @@ Dispatches an event to the configured runner.
--------------------


### addListener('backgroundRunnerNotificationReceived', ...)

```typescript
addListener(eventName: 'backgroundRunnerNotificationReceived', listenerFunc: (event: NotificationActionEvent) => void) => any
```

Add a listener for notification actions.

| Param | Type |
| ------------------ | ----------------------------------------------------------------------------------------------- |
| **`eventName`** | <code>'backgroundRunnerNotificationReceived'</code> |
| **`listenerFunc`** | <code>(event: <a href="#notificationactionevent">NotificationActionEvent</a>) =&gt; void</code> |

**Returns:** <code>any</code>

**Since:** 2.1.1

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


### removeNotificationListeners()

```typescript
removeNotificationListeners() => any
```

Remove notification action listeners for this plugin.

**Returns:** <code>any</code>

**Since:** 2.1.1

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


### Interfaces


Expand Down Expand Up @@ -377,6 +414,21 @@ Dispatches an event to the configured runner.
| **`details`** | <code>{ [key: string]: any; }</code> | | |


#### NotificationActionEvent

| Prop | Type |
| -------------------- | ------------------- |
| **`actionTypeId`** | <code>string</code> |
| **`notificationId`** | <code>number</code> |


#### PluginListenerHandle

| Prop | Type |
| ------------ | ------------------------- |
| **`remove`** | <code>() =&gt; any</code> |


### Type Aliases


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import android.content.Intent
import com.getcapacitor.Bridge


@CapacitorPlugin(
Expand Down Expand Up @@ -125,4 +127,46 @@ class BackgroundRunnerPlugin: Plugin() {
fun registerBackgroundTask(call: PluginCall) {
call.resolve()
}

override fun handleOnNewIntent(intent: Intent) {
super.handleOnNewIntent(intent)

Log.d("BackgroundRunner", "handleOnNewIntent with action: ${intent.action}")

if (intent.action == ".NOTIFICATION_CLICKED") {
val actionTypeId = intent.getStringExtra("actionTypeId")
val notificationId = intent.getIntExtra("notificationId", -1)

// Create notification action data
val notificationAction = if (actionTypeId != null) {
JSObject().apply {
put("actionTypeId", actionTypeId)
put("notificationId", notificationId)
}
} else null

// Launch the app using proper intent flags
val packageName = context.packageName
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)?.apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
}

if (launchIntent != null) {
context.startActivity(launchIntent)

// Notify listeners about the action with retention
notificationAction?.let {
notifyListeners("backgroundRunnerNotificationReceived", it, true)
}
}
}
}

@PluginMethod
fun removeNotificationListeners(call: PluginCall) {
notifyListeners("backgroundRunnerNotificationReceived", null)
call.resolve()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,28 +93,11 @@ class Notifications(context: Context) : NotificationsAPI {
builder.setSmallIcon(it.smallIcon(this.context, getDefaultSmallIcon()))

// Add action intent handling
val actionIntent = if (it.actionTypeId != null) {
try {
Intent(it.actionTypeId).apply {
`package` = context.packageName
}
} catch (e: Exception) {
context.packageManager.getLaunchIntentForPackage(context.packageName)
?: Intent().apply {
setPackage(context.packageName)
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
}
}
} else {
context.packageManager.getLaunchIntentForPackage(context.packageName)
?: Intent().apply {
setPackage(context.packageName)
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
}
val actionIntent = Intent(".NOTIFICATION_CLICKED").apply {
`package` = context.packageName
putExtra("actionTypeId", it.actionTypeId)
putExtra("notificationId", it.id)
}
actionIntent.addCategory(Intent.CATEGORY_DEFAULT)

val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
Expand Down
21 changes: 21 additions & 0 deletions packages/capacitor-plugin/src/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// <reference types="@capacitor/cli" />

import type { PermissionState } from '@capacitor/core';
import type { PluginListenerHandle } from '@capacitor/core';

export interface BackgroundRunnerConfig {
/**
Expand Down Expand Up @@ -130,6 +131,11 @@ export interface RegisterBackgroundTaskOptions {
runner: BackgroundRunnerConfig;
}

export interface NotificationActionEvent {
actionTypeId: string;
notificationId: number;
}

export interface BackgroundRunnerPlugin {
/**
* Check permissions for the various Capacitor device APIs.
Expand All @@ -151,6 +157,21 @@ export interface BackgroundRunnerPlugin {
* @since 1.0.0
*/
dispatchEvent<T = void>(options: DispatchEventOptions): Promise<T>;
/**
* Add a listener for notification actions.
*
* @since 2.1.1
*/
addListener(
eventName: 'backgroundRunnerNotificationReceived',
listenerFunc: (event: NotificationActionEvent) => void,
): Promise<PluginListenerHandle>;
/**
* Remove notification action listeners for this plugin.
*
* @since 2.1.1
*/
removeNotificationListeners(): Promise<void>;
}

declare module '@capacitor/cli' {
Expand Down
17 changes: 14 additions & 3 deletions packages/capacitor-plugin/src/web.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { WebPlugin } from '@capacitor/core';
import { WebPlugin, PluginListenerHandle } from '@capacitor/core';

import type {
BackgroundRunnerPlugin,
DispatchEventOptions,
PermissionStatus,
NotificationActionEvent,
} from './definitions';

export class BackgroundRunnerWeb
extends WebPlugin
implements BackgroundRunnerPlugin
{
implements BackgroundRunnerPlugin {
checkPermissions(): Promise<PermissionStatus> {
throw new Error('not available on web');
}
Expand All @@ -25,4 +25,15 @@ export class BackgroundRunnerWeb
dispatchEvent<T = void>(_options: DispatchEventOptions): Promise<T> {
throw new Error('not available on web');
}

async addListener(
_eventName: 'backgroundRunnerNotificationReceived',
_listenerFunc: (event: NotificationActionEvent) => void,
): Promise<PluginListenerHandle> {
throw new Error('not available on web');
}

async removeNotificationListeners(): Promise<void> {
throw new Error('not available on web');
}
}

0 comments on commit 6fde8cc

Please sign in to comment.