Skip to content

Commit da8c5c6

Browse files
refactor(android): replace IntercomMessagingService with static IntercomFcmHelper (#7)
* feat(android): add native push notification handling for Intercom Add IntercomMessagingService (FirebaseMessagingService with priority 100) that handles Intercom pushes natively via handlePush(), ensuring notifications display correctly when the app is backgrounded. Non-Intercom messages are forwarded to @capacitor/push-notifications via reflection. Add IntercomInitProvider (ContentProvider) that reads Intercom keys from capacitor.config.json and initializes the SDK at app startup, before any Activity launches. This prevents crashes when tapping a notification on cold start (IntercomRootActivity requires Intercom to be initialized). Users just need to add their keys to capacitor.config.json: "plugins": { "CapgoIntercom": { "androidApiKey": "android_sdk-...", "androidAppId": "your-app-id" } } * fix: address PR review comments on IntercomInitProvider and manifest - Return true from IntercomInitProvider.onCreate() per ContentProvider contract - Use ByteArrayOutputStream loop instead of unreliable is.available()/is.read() - Use try-with-resources for proper InputStream cleanup - Use fully-qualified class names in AndroidManifest.xml * refactor(android): replace IntercomMessagingService with static IntercomFcmHelper Android only allows one FirebaseMessagingService, so the plugin should not register its own service — it conflicts with other FCM-dependent plugins (e.g. @capacitor/push-notifications, Pushwoosh, OneSignal). Instead, provide a static helper class (IntercomFcmHelper) that app developers call from their own FirebaseMessagingService, following the same pattern as Pushwoosh's PushwooshFcmHelper. Changes: - Delete IntercomMessagingService and its manifest registration - Add IntercomFcmHelper with isIntercomPush(), onMessageReceived(), onNewToken() - Change firebase-messaging dependency to compileOnly (app provides it) * docs: add Android push notification setup guide for IntercomFcmHelper Document how to create a custom FirebaseMessagingService that routes Intercom pushes via IntercomFcmHelper while remaining compatible with other Firebase plugins.
1 parent 5d1422c commit da8c5c6

File tree

5 files changed

+207
-0
lines changed

5 files changed

+207
-0
lines changed

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,53 @@ The Intercom iOS SDK (`~> 19.0`) is included automatically via CocoaPods or Swif
6969

7070
The Intercom Android SDK (`17.4.2`) is included automatically via Gradle.
7171

72+
#### Push Notifications
73+
74+
Since Android only allows **one** `FirebaseMessagingService`, this plugin does **not** register its own. Instead it provides `IntercomFcmHelper` — a static helper you call from your app's service.
75+
76+
1. Create your own `FirebaseMessagingService` in your app (e.g. `app/src/main/java/.../MyFirebaseMessagingService.java`):
77+
78+
```java
79+
package com.your.app;
80+
81+
import com.google.firebase.messaging.FirebaseMessagingService;
82+
import com.google.firebase.messaging.RemoteMessage;
83+
import app.capgo.capacitor.intercom.IntercomFcmHelper;
84+
85+
public class MyFirebaseMessagingService extends FirebaseMessagingService {
86+
87+
@Override
88+
public void onMessageReceived(RemoteMessage remoteMessage) {
89+
if (IntercomFcmHelper.isIntercomPush(remoteMessage)) {
90+
IntercomFcmHelper.onMessageReceived(this, remoteMessage);
91+
} else {
92+
// Handle other push SDKs (e.g. @capacitor/push-notifications)
93+
// or your own notification logic here
94+
}
95+
}
96+
97+
@Override
98+
public void onNewToken(String token) {
99+
IntercomFcmHelper.onNewToken(this, token);
100+
// Forward token to other SDKs if needed
101+
}
102+
}
103+
```
104+
105+
2. Register it in your app's `AndroidManifest.xml`:
106+
107+
```xml
108+
<service
109+
android:name=".MyFirebaseMessagingService"
110+
android:exported="false">
111+
<intent-filter>
112+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
113+
</intent-filter>
114+
</service>
115+
```
116+
117+
This approach is compatible with any other Firebase plugin — you control the routing.
118+
72119
## API
73120

74121
<docgen-index>

android/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ dependencies {
5555
annotationProcessor project(':capacitor-android')
5656
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
5757
implementation "io.intercom.android:intercom-sdk:$intercomSdkVersion"
58+
compileOnly "com.google.firebase:firebase-messaging:24.1.1"
5859
testImplementation "junit:junit:$junitVersion"
5960
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
6061
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
<application>
3+
<provider
4+
android:name="app.capgo.capacitor.intercom.IntercomInitProvider"
5+
android:authorities="${applicationId}.intercom-init"
6+
android:exported="false"
7+
android:initOrder="100" />
8+
</application>
29
</manifest>
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package app.capgo.capacitor.intercom;
2+
3+
import android.app.Application;
4+
import android.util.Log;
5+
import androidx.annotation.NonNull;
6+
import com.google.firebase.messaging.RemoteMessage;
7+
import io.intercom.android.sdk.push.IntercomPushClient;
8+
import java.util.Map;
9+
10+
/**
11+
* Static helper for routing Firebase Cloud Messaging events to Intercom.
12+
*
13+
* <p>Since Android allows only one {@code FirebaseMessagingService}, your app must
14+
* create its own service and call these methods to forward Intercom pushes.
15+
*
16+
* <h3>Usage</h3>
17+
* <pre>{@code
18+
* public class MyFirebaseMessagingService extends FirebaseMessagingService {
19+
*
20+
* @Override
21+
* public void onMessageReceived(RemoteMessage remoteMessage) {
22+
* if (IntercomFcmHelper.isIntercomPush(remoteMessage)) {
23+
* IntercomFcmHelper.onMessageReceived(this, remoteMessage);
24+
* } else {
25+
* // handle your own / other SDK pushes
26+
* }
27+
* }
28+
*
29+
* @Override
30+
* public void onNewToken(String token) {
31+
* IntercomFcmHelper.onNewToken(this, token);
32+
* // also forward token to other SDKs if needed
33+
* }
34+
* }
35+
* }</pre>
36+
*/
37+
public final class IntercomFcmHelper {
38+
39+
private static final String TAG = "IntercomFcmHelper";
40+
private static final IntercomPushClient pushClient = new IntercomPushClient();
41+
42+
private IntercomFcmHelper() {}
43+
44+
/**
45+
* Returns {@code true} if this message was sent by Intercom.
46+
*/
47+
public static boolean isIntercomPush(@NonNull RemoteMessage remoteMessage) {
48+
return pushClient.isIntercomPush(remoteMessage.getData());
49+
}
50+
51+
/**
52+
* Passes an Intercom push message to the SDK for display.
53+
* Call this only when {@link #isIntercomPush} returns {@code true}.
54+
*/
55+
public static void onMessageReceived(@NonNull android.content.Context context, @NonNull RemoteMessage remoteMessage) {
56+
Map<String, String> data = remoteMessage.getData();
57+
if (pushClient.isIntercomPush(data)) {
58+
Log.d(TAG, "Handling Intercom push");
59+
Application app = (Application) context.getApplicationContext();
60+
pushClient.handlePush(app, data);
61+
}
62+
}
63+
64+
/**
65+
* Forwards a new FCM token to Intercom so the device can receive pushes.
66+
*/
67+
public static void onNewToken(@NonNull android.content.Context context, @NonNull String token) {
68+
Log.d(TAG, "Forwarding FCM token to Intercom");
69+
Application app = (Application) context.getApplicationContext();
70+
pushClient.sendTokenToIntercom(app, token);
71+
}
72+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package app.capgo.capacitor.intercom;
2+
3+
import android.app.Application;
4+
import android.content.ContentProvider;
5+
import android.content.ContentValues;
6+
import android.content.Context;
7+
import android.database.Cursor;
8+
import android.net.Uri;
9+
import android.util.Log;
10+
import io.intercom.android.sdk.Intercom;
11+
import java.io.ByteArrayOutputStream;
12+
import java.io.InputStream;
13+
import org.json.JSONObject;
14+
15+
/**
16+
* Auto-initializes Intercom at app startup by reading keys from capacitor.config.json.
17+
*
18+
* ContentProviders run before Application.onCreate(), which means Intercom is ready
19+
* before any Activity launches — including IntercomRootActivity when the user taps
20+
* a push notification on a cold start.
21+
*
22+
* Users just need to add their keys to capacitor.config.json:
23+
*
24+
* "plugins": {
25+
* "CapgoIntercom": {
26+
* "androidApiKey": "android_sdk-...",
27+
* "androidAppId": "your-app-id"
28+
* }
29+
* }
30+
*/
31+
public class IntercomInitProvider extends ContentProvider {
32+
33+
private static final String TAG = "IntercomInitProvider";
34+
35+
@Override
36+
public boolean onCreate() {
37+
Context context = getContext();
38+
if (context == null) return false;
39+
40+
try (InputStream is = context.getAssets().open("capacitor.config.json")) {
41+
ByteArrayOutputStream result = new ByteArrayOutputStream();
42+
byte[] buf = new byte[4096];
43+
int len;
44+
while ((len = is.read(buf)) != -1) {
45+
result.write(buf, 0, len);
46+
}
47+
48+
JSONObject config = new JSONObject(result.toString("UTF-8"));
49+
JSONObject plugins = config.optJSONObject("plugins");
50+
if (plugins == null) return true;
51+
52+
JSONObject intercomConfig = plugins.optJSONObject("CapgoIntercom");
53+
if (intercomConfig == null) return true;
54+
55+
String apiKey = intercomConfig.optString("androidApiKey", "");
56+
String appId = intercomConfig.optString("androidAppId", "");
57+
58+
if (!apiKey.isEmpty() && !appId.isEmpty()) {
59+
Application app = (Application) context.getApplicationContext();
60+
Intercom.initialize(app, apiKey, appId);
61+
Log.d(TAG, "Intercom initialized from capacitor.config.json");
62+
}
63+
} catch (Exception e) {
64+
Log.w(TAG, "Could not auto-initialize Intercom: " + e.getMessage());
65+
}
66+
67+
return true;
68+
}
69+
70+
@Override
71+
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; }
72+
@Override
73+
public String getType(Uri uri) { return null; }
74+
@Override
75+
public Uri insert(Uri uri, ContentValues values) { return null; }
76+
@Override
77+
public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; }
78+
@Override
79+
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }
80+
}

0 commit comments

Comments
 (0)