Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .pubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Sub-plugins — published as separate packages, not part of this package
PointSDK-Flutter-Push/

# Internal docs not intended for pub.dev
push.md

# Build outputs & caches
build/
.dart_tool/
.pub/
.pub-cache/

# Flutter-generated files
.flutter-plugins
.flutter-plugins-dependencies
.packages

# IDE
.idea/
*.iml
.vscode/

# Android build artefacts
**/android/.gradle
**/android/local.properties
**/android/captures/

# iOS / Xcode artefacts
**/ios/**/Pods/
**/ios/**/DerivedData/
**/ios/**/xcuserdata/
**/ios/**/.symlinks/
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/flutter_export_environment.sh
**/ios/Runner/GeneratedPluginRegistrant.*

# macOS
.DS_Store
._*

# Misc
*.log
*.class

39 changes: 39 additions & 0 deletions PointSDK-Flutter-Push/.pubignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Build outputs & caches
build/
.dart_tool/
.pub/
.pub-cache/

# Flutter-generated files
.flutter-plugins
.flutter-plugins-dependencies
.packages

# IDE
.idea/
*.iml
.vscode/

# Android build artefacts
**/android/.gradle
**/android/local.properties
**/android/captures/

# iOS / Xcode artefacts
**/ios/**/Pods/
**/ios/**/DerivedData/
**/ios/**/xcuserdata/
**/ios/**/.symlinks/
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/flutter_export_environment.sh
**/ios/Runner/GeneratedPluginRegistrant.*

# macOS
.DS_Store
._*

# Misc
*.log
*.class


129 changes: 129 additions & 0 deletions PointSDK-Flutter-Push/INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# `bluedot_point_sdk_push` — Android Integration Guide

## Overview

The `bluedot_point_sdk_push` Flutter plugin bridges the Bluedot push notifications module
to Flutter. Because a Flutter app may already use Firebase Cloud Messaging (FCM) for its own
purposes, **the plugin does not register a `FirebaseMessagingService`**. Instead, you wire up
FCM in your own app and forward the relevant events to the plugin with two method calls.

---

## Step 1 — Add the plugin

In your app's `pubspec.yaml`:

```yaml
dependencies:
bluedot_point_sdk: ^2.0.0
bluedot_point_sdk_push: ^1.0.0
```

---

## Step 2 — Set up Firebase

If your app doesn't use Firebase yet, follow the
[official Android FCM setup guide](https://firebase.google.com/docs/cloud-messaging/android/get-started)
to add Firebase to your project (`google-services.json`, `google-services` Gradle plugin, etc.).

> **Android 13 (API 33) and above:** You must declare `POST_NOTIFICATIONS` in your manifest
> and request it at runtime before notifications can be displayed.
> See the [Android documentation](https://developer.android.com/training/permissions/requesting).

---

## Step 3 — Implement `FirebaseMessagingService`

Create a `FirebaseMessagingService` subclass in your app's Android source and forward the two
FCM events to the plugin using `BluedotPushPlugin`:

```kotlin
// android/app/src/main/kotlin/com/yourcompany/yourapp/MyFirebaseMessagingService.kt

package com.yourcompany.yourapp

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.bluedot.bluedot_point_sdk_push.BluedotPushPlugin

class MyFirebaseMessagingService : FirebaseMessagingService() {

override fun onNewToken(token: String) {
// Forward the new token to Bluedot.
BluedotPushPlugin.onNewFcmToken(token, this)

// Add any other token-refresh handling here.
}

override fun onMessageReceived(remoteMessage: RemoteMessage) {
// Let Bluedot handle its own messages first.
// Returns true if the message was a Bluedot push notification.
if (BluedotPushPlugin.onMessageReceived(remoteMessage, this)) return

// Handle your own non-Bluedot FCM messages here.
}
}
```

---

## Step 4 — Register the service in `AndroidManifest.xml`

Add the service declaration inside the `<application>` tag of your
`android/app/src/main/AndroidManifest.xml`:

```xml
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
```

> The `AppPushNotificationsReceiver` (required for event callbacks to Flutter) is registered
> automatically by the plugin's own manifest — no additional entry is needed for it.

---

## Step 5 — Listen for push notification events in Flutter

Set up the event listener early in your app, before any notifications can arrive
(e.g. in `main.dart` or your root widget's `initState`):

```dart
import 'package:flutter/services.dart';
import 'package:bluedot_point_sdk_push/bluedot_point_sdk_push.dart';

void _initPushListener() {
const channel = MethodChannel(BluedotPointSdkPush.pushNotifications);
channel.setMethodCallHandler((call) async {
final data = Map<String, dynamic>.from(call.arguments as Map);
switch (call.method) {
case PushNotificationEvents.onNotificationReceived:
// Notification delivered to the device.
print('Received: ${data['title']} — ${data['body']}');
print('Campaign: ${data['campaignId']}, Zone: ${data['zoneId']}');
break;
case PushNotificationEvents.onNotificationClicked:
// User tapped the notification.
print('Clicked: ${data['notificationId']}');
break;
}
});
}
```

### Event payload fields

| Field | Type | Description |
|------------------|----------------------|-----------------------------------------|
| `title` | `String` | Notification title |
| `body` | `String` | Notification body text |
| `pushVersion` | `String` | Push schema version |
| `campaignId` | `String` | Campaign UUID |
| `zoneId` | `String` | Zone UUID |
| `notificationId` | `String` | Notification UUID |
| `data` | `Map<String, String>`| Custom key-value pairs from the payload |
75 changes: 75 additions & 0 deletions PointSDK-Flutter-Push/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
group 'io.bluedot.bluedot_point_sdk_push'
version '1.0.0'

buildscript {
ext.kotlin_version = '2.3.0'
ext.sdk_version = '18.0.0'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:9.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

rootProject.allprojects {
repositories {
mavenLocal()
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
namespace 'io.bluedot.bluedot_point_sdk_push'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

defaultConfig {
minSdk 29
compileSdk 36
}
}

// ---------------------------------------------------------------------------
// Firebase BOM version
//
// The consumer app is responsible for applying the google-services plugin and
// placing google-services.json in their own app module. This library only
// needs the firebase-messaging artifact on the compile classpath; it does NOT
// need (or want) the google-services Gradle plugin.
//
// Override the BOM version from your app's build.gradle / gradle.properties:
// ext.firebaseBomVersion = '34.0.0'
// ---------------------------------------------------------------------------
def firebaseBomVersion = rootProject.hasProperty('firebaseBomVersion') ?
rootProject.property('firebaseBomVersion') : (project.hasProperty('firebaseBomVersion') ?
project.property('firebaseBomVersion') : '34.12.0')

dependencies {
// Core Point SDK — must match the version used by the base plugin.
implementation "com.gitlab.bluedotio.android:point_sdk_android:$sdk_version"


// Push notifications module and its Firebase dependency.
// These are the ONLY place in the plugin ecosystem where these artifacts are declared,
// so they are only resolved for apps that add bluedot_point_sdk_push.
implementation "com.gitlab.bluedotio.android:point_sdk_push:$sdk_version"
implementation platform("com.google.firebase:firebase-bom:${firebaseBomVersion}")
implementation "com.google.firebase:firebase-messaging"
}

48 changes: 48 additions & 0 deletions PointSDK-Flutter-Push/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<!--
AppPushNotificationsEventReceiver: receives push notification callbacks
(onNotificationReceived / onNotificationClicked) from the Bluedot Push SDK
and bridges them to the React Native JS layer.
-->
<receiver
android:name="io.bluedot.pushnotifications.AppPushNotificationsEventReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="io.bluedot.point.PUSHNOTIFICATIONS" />
</intent-filter>
</receiver>

<!--
DefaultMessagingService: automatic FCM handler for Pattern A (no @react-native-firebase).

Registered at intent-filter priority -1. Any FirebaseMessagingService the consumer
app registers without an explicit priority defaults to 0, which takes precedence.
This service is never reached once the consumer registers their own.

Pattern B - @react-native-firebase/messaging:
That library registers its own service at priority 0, pre-empting this one.
Forward messages to Bluedot via the JS bridge instead:
PushNotifications.onMessageReceived(msg) and PushNotifications.onNewFcmToken(token).

Pattern C - multiple push sources (e.g. Airship + Bluedot):
Register your own FirebaseMessagingService (default priority 0). Route Bluedot
messages to ServiceManager.getInstance(this).pushNotificationsManager inside it.
This service is pre-empted automatically. No manifest changes needed.

To remove this service from the merged manifest entirely, add a service element
in your app AndroidManifest.xml with tools:node="remove" targeting this class name.
-->
<service
android:name="io.bluedot.pushnotifications.DefaultMessagingService"
android:exported="false">
<intent-filter android:priority="-1">
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>

</manifest>

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.bluedot.pushnotifications

import android.content.Context
import au.com.bluedot.point.api.push.model.RezolvePushData
import com.rezolve.pushnotifications.PushNotificationsEventReceiver

/**
* Receives Bluedot push notification callbacks and forwards them to Flutter via
* [BluedotPushPlugin.pushNotificationsChannel].
*
* Registered automatically in this plugin's AndroidManifest.xml — no manual
* manifest entry required in the consuming app.
*/
class AppPushNotificationsEventReceiver : PushNotificationsEventReceiver() {

override fun onNotificationReceived(rezolvePushData: RezolvePushData, context: Context) {
BluedotPushPlugin.pushNotificationsChannel?.invokeMethod(
"onNotificationReceived",
rezolvePushData.toMap()
)
}

override fun onNotificationClicked(rezolvePushData: RezolvePushData, context: Context) {
BluedotPushPlugin.pushNotificationsChannel?.invokeMethod(
"onNotificationClicked",
rezolvePushData.toMap()
)
}

private fun RezolvePushData.toMap(): Map<String, Any?> = mapOf(
"title" to title,
"body" to body,
"pushVersion" to pushVersion,
"campaignId" to campaignId,
"zoneId" to zoneId,
"notificationId" to notificationId,
"data" to data
)
}

Loading