Skip to content

Commit

Permalink
Add Android notification plugin setup
Browse files Browse the repository at this point in the history
  • Loading branch information
dangeross committed Mar 27, 2024
1 parent 4647cab commit 6213ebb
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
- [Register a webhook](notifications/register_webhook.md)
- [Project Integration](notifications/setup_plugin.md)
- [Android](notifications/android_setup.md)
- [Foreground Service](notifications/android_service.md)
- [Notification Plugin](notifications/android_plugin.md)
- [iOS](notifications/ios_setup.md)
- [Notification Service Extension](notifications/ios_service.md)
- [Notification Plugin](notifications/ios_plugin.md)
Expand Down
145 changes: 145 additions & 0 deletions src/notifications/android_plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Add the Android Notification Plugin

Add the `breez-sdk` dependency to your application's `build.gradle` file in the `app` directory.

```gradle
android {
defaultConfig {
// Add a build config field to read the Breez API key
// from a git ignored `gradle.properties` file
buildConfigField "String", "BREEZ_SDK_API_KEY", project.property('BREEZ_SDK_API_KEY')
}
// This might help building if duplicate libraries are found
packagingOptions {
pickFirst "lib/armeabi-v7a/libc++_shared.so"
pickFirst "lib/arm64-v8a/libc++_shared.so"
pickFirst "lib/x86/libc++_shared.so"
pickFirst "lib/x86_64/libc++_shared.so"
exclude "META-INF/*"
}
}
dependencies {
// Add the breez-sdk dependency
implementation "com.github.breez:breez-sdk"
}
```

## Integrate the Notification Plugin

You're ready to add some Kotlin code to implement the Notification Plugin in your application. In the example below we are using the `FirebaseMessagingService` to receive the message intents. First lets implement the Notification Plugin's `MessagingService` class along with `FirebaseMessagingService`.

```kotlin
package com.example.application

import android.annotation.SuppressLint
import android.content.Intent
import androidx.core.content.ContextCompat
import breez_sdk_notification.Constants
import breez_sdk_notification.Message
import breez_sdk_notification.MessagingService
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage

@SuppressLint("MissingFirebaseInstanceTokenRefresh")
class ExampleFcmService : MessagingService, FirebaseMessagingService() {
companion object {
private const val TAG = "FcmService"
}

// Override the `onMessageReceived` to handle the remote message
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)

// Check if the message is high priority and can be handled
if (remoteMessage.priority == RemoteMessage.PRIORITY_HIGH) {
remoteMessage.asMessage()?.also { message ->
// Call `startServiceIfNeeded` to check if the foreground
// service is needed depending on the message type and
// foreground state of the application
startServiceIfNeeded(applicationContext, message)
}
}
}

// A helper function the convert the `RemoteMessage`
// to a notification plugin 'Message'
private fun RemoteMessage.asMessage(): Message? {
return data[Constants.MESSAGE_DATA_TYPE]?.let {
Message(
data[Constants.MESSAGE_DATA_TYPE], data[Constants.MESSAGE_DATA_PAYLOAD]
)
}
}

// Override the `startForegroundService` function to start the foreground service
// using the `ExampleForegroundService` handler
override fun startForegroundService(message: Message) {
val intent = Intent(applicationContext, ExampleForegroundService::class.java)
intent.putExtra(Constants.EXTRA_REMOTE_MESSAGE, message)
ContextCompat.startForegroundService(applicationContext, intent)
}
}
```

Now lets add the foreground service implementation. This should implement the notification plugin `ForegroundService` class, which handles the incoming notification intent and processes the event. To properly implement this, your class needs to override the `onCreate`, `getConnectRequest` and `getServiceConfig` functions. The `getConnectRequest` function is called by the `ForegroundService` to get a BreezSDK `ConnectRequest` which contains the data necessary to connect the SDK to the node. This data includes the Breez API key, the `Config` with it's workingDir and the node seed.

<div class="warning">
<h4>Developer note</h4>
In Android reading from secured storage can vary a lot depending if it is a Kotlin, Flutter or React Native based application and the dependencies used to write to the secured storage. Consult the dependency used to write to the secured storage on how to read data back from them.
</div>

```kotlin
package com.example.application

import breez_sdk.ConnectRequest
import breez_sdk.EnvironmentType
import breez_sdk.GreenlightNodeConfig
import breez_sdk.NodeConfig
import breez_sdk.defaultConfig
import breez_sdk.mnemonicToSeed
import breez_sdk_notification.ForegroundService
import breez_sdk_notification.NotificationHelper.Companion.registerNotificationChannels
import breez_sdk_notification.ServiceConfig

class ExampleForegroundService : ForegroundService() {
companion object {
private const val TAG = "ForegroundService"
private const val ACCOUNT_MNEMONIC = "BREEZ_SDK_SEED_MNEMONIC"
}

// Override the `onCreate` function
override fun onCreate() {
super.onCreate()
// Register the default notification channels
registerNotificationChannels(applicationContext, DEFAULT_CLICK_ACTION)
}

// Override the `getConnectRequest` function
override fun getConnectRequest(): ConnectRequest? {
// Get the Breez API key from the build config
val apiKey = BuildConfig.BREEZ_SDK_API_KEY
val glNodeConf = GreenlightNodeConfig(null, null)
val nodeConf = NodeConfig.Greenlight(glNodeConf)
val config = defaultConfig(EnvironmentType.PRODUCTION, apiKey, nodeConf)

// Set the workingDir as the same directory as the main application
config.workingDir = "${applicationContext.applicationInfo.dataDir}/breezSdk"

// Get the mnemonic from secured storage using an implementation of
// `readSecuredValue` depending on how data is written to secured storage.
// See Developer Note
return readSecuredValue(applicationContext, ACCOUNT_MNEMONIC)
?.let { mnemonic ->
ConnectRequest(config, mnemonicToSeed(mnemonic))
}
}

// Override the `getServiceConfig` function
override fun getServiceConfig(): ServiceConfig? {
// For now just return the default config
return ServiceConfig.default()
}
}
```
37 changes: 37 additions & 0 deletions src/notifications/android_service.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Android Foreground Service

In the `AndroidManifest.xml` file of the application's `app/src/main` directory, add the user permissions necessary to handle notifications `POST_NOTIFICATIONS` as a foreground service `FOREGROUND_SERVICE`. Then to your main application add two services, one to handle messaging events and one to handle the foreground service.

```xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<!-- Add these permissions -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application
android:label="Custom App"
android:name="${applicationName}"
android:roundIcon="@mipmap/ic_launcher"
android:icon="@mipmap/ic_launcher">

<!-- Add a service to handle messaging events -->
<service
android:name=".ExampleFcmService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>

<!-- Add a service to handle the foreground service -->
<service
android:name=".ExampleForegroundService"
android:foregroundServiceType="shortService"
android:exported="false"
android:stopWithTask="false">
</service>
</application>
</manifest>
```
8 changes: 7 additions & 1 deletion src/notifications/android_setup.md
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# Android
# Setup the Android Notification Plugin

In order to add the Notification Plugin to your Android application, first you need to setup the Foreground Service, then add the Notification Plugin dependency and integrate it:

## Next Steps
- **[Setup the Foreground Service](android_service.md)**
- **[Add the Notification Plugin](android_plugin.md)**
4 changes: 2 additions & 2 deletions src/notifications/changing_strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The Notification Plugin uses a set of identifiers and default strings to display

In the `string.xml` file of the application's `res/values` directory, add the key/value:

```
```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="lnurl_pay_metadata_plain_text">Pay to Custom App</string>
Expand All @@ -18,7 +18,7 @@ You can find the current identifiers and default strings [here](https://github.c

In the `Info.plist` file of the `NotificationService` target, add the key/value:

```
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
Expand Down
3 changes: 2 additions & 1 deletion src/notifications/ios_plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Add the `BreezSDK` cocoapod to your iOS Podfile, with the target `NotificationService`. You can add any other dependencies your require here also, for example `KeychainAccess` to read the saved mnemonic from the keychain.

```
```podfile
target 'NotificationService' do
pod 'BreezSDK'
pod 'KeychainAccess'
Expand Down Expand Up @@ -42,6 +42,7 @@ fileprivate let accountMnemonic: String = "BREEZ_SDK_SEED_MNEMONIC"
fileprivate let accountApiKey: String = "BREEZ_SDK_API_KEY"

class NotificationService: SDKNotificationService {
// Override the `getConnectRequest` function
override func getConnectRequest() -> ConnectRequest? {
// Get the Breez API key from the target bundle's Info.plist
guard let apiKey = Bundle.main.object(forInfoDictionaryKey: accountApiKey) as? String else {
Expand Down

0 comments on commit 6213ebb

Please sign in to comment.