Skip to content
This repository was archived by the owner on Apr 4, 2023. It is now read-only.

Commit b4bee77

Browse files
#54 Support for Push Notifications - Android impl done!
1 parent 191c6d3 commit b4bee77

39 files changed

+777
-19
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
platforms/android/libraryproject/

docs/MESSAGING.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ Version 3.3.0 of this plugin added FCM support (which is the successor of GCM).
1111
Although using push messages in your Firebase app is really easy setting it up is not. Traditionally, especially for iOS.
1212

1313
### Android
14-
Work in progress.
14+
Uncomment `firebase-messaging` in [include.gradle](../platforms/android/include.gradle)
1515

1616
### iOS
17+
Uncomment `Firebase/Messaging` in [Podfile](../platforms/ios/Podfile)
1718

1819
#### Receiving remote notifications in the background
1920
Open `app/App_Resources/iOS/Info.plist` and add this to the bottom:
@@ -43,6 +44,7 @@ Any pending notifications (while your app was not in the foreground) will trigge
4344
console.log("Body: " + message.body);
4445
// if your server passed a custom property called 'foo', then do this:
4546
console.log("Value of 'foo': " + message.foo);
47+
}
4648
});
4749
```
4850

@@ -52,6 +54,41 @@ You don't _have_ to provide the handler during `init` - you can also do it throu
5254
firebase.addOnMessageReceivedCallback(
5355
function(message) {
5456
// ..
55-
});
57+
}
58+
);
59+
```
60+
61+
### Pushing to individual devices
62+
If you want to send push messages to individual devices, either from your own backend or the FCM console, you need the push token.
63+
64+
Similarly to the message callback you can either wire this through `init` or as a separate function:
65+
66+
```js
67+
firebase.init({
68+
onPushTokenReceivedCallback: function(token) {
69+
console.log("Firebase push token: " + token);
70+
}
5671
});
5772
```
73+
74+
.. or:
75+
76+
```js
77+
firebase.addOnPushTokenReceivedCallback(
78+
function(token) {
79+
// ..
80+
}
81+
);
82+
```
83+
84+
## Testing
85+
Using the Firebase Console gives you most flexibility, but you can quickly and easily test from the command line as well:
86+
87+
```
88+
curl -X POST --header "Authorization: key=SERVER_KEY" --Header "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d "{\"notification\":{\"title\": \"My title\", \"text\": \"My text\", \"sound\": \"default\"}, \"priority\": \"High\", \"to\": \"DEVICE_TOKEN\"}"
89+
```
90+
91+
* SERVER_KEY: see below
92+
* DEVICE_TOKEN: the on you got in `addOnPushTokenReceivedCallback` or `init`'s `onPushTokenReceivedCallback`
93+
94+
<img src="images/push-server-key.png" width="459px" height="220px" alt="Push server key"/>

docs/images/push-server-key.png

98.5 KB
Loading

firebase-common.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,10 @@ firebase.QueryRangeType = {
2626
};
2727

2828
firebase.instance = null;
29-
3029
firebase.firebaseRemoteConfig = null;
31-
3230
firebase.authStateListeners = [];
33-
3431
firebase._receivedNotificationCallback = null;
3532

36-
firebase._pendingNotifications = [];
37-
3833
firebase.addAuthStateListener = function(listener) {
3934
if (firebase.authStateListeners.indexOf(listener) === -1) {
4035
firebase.authStateListeners.push(listener);

firebase.android.js

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,41 @@
11
var appModule = require("application");
22
var firebase = require("./firebase-common");
33

4+
firebase._launchNotification = null;
5+
6+
(function() {
7+
if (typeof(com.google.firebase.messaging) === "undefined") {
8+
return;
9+
}
10+
appModule.onLaunch = function(intent) {
11+
var extras = intent.getExtras();
12+
if (extras !== null) {
13+
var result = {
14+
foreground: false
15+
};
16+
17+
var iterator = extras.keySet().iterator();
18+
while (iterator.hasNext()) {
19+
var key = iterator.next();
20+
if (key !== "from" && key !== "collapse_key") {
21+
result[key] = extras.get(key);
22+
}
23+
}
24+
25+
// in case this was a cold start we don't have the _receivedNotificationCallback yet
26+
if (firebase._receivedNotificationCallback === null) {
27+
firebase._launchNotification = result;
28+
} else {
29+
// add a little delay just to make sure clients alerting this message will see it as the UI needs to settle
30+
setTimeout(function() {
31+
firebase._receivedNotificationCallback(result);
32+
});
33+
}
34+
}
35+
};
36+
37+
})();
38+
439
firebase.toHashMap = function(obj) {
540
var node = new java.util.HashMap();
641
for (var property in obj) {
@@ -134,13 +169,12 @@ firebase.init = function (arg) {
134169

135170
// Firebase notifications (FCM)
136171
if (typeof(com.google.firebase.messaging) !== "undefined") {
137-
console.log("--- has messaging!");
138-
// TODO see iOS:
139-
// firebase._addObserver(kFIRInstanceIDTokenRefreshNotification, firebase._onTokenRefreshNotification);
140172
if (arg.onMessageReceivedCallback !== undefined) {
141-
console.log("--- adding messaging callback!");
142173
firebase.addOnMessageReceivedCallback(arg.onMessageReceivedCallback);
143174
}
175+
if (arg.onPushTokenReceivedCallback !== undefined) {
176+
firebase.addOnPushTokenReceivedCallback(arg.onPushTokenReceivedCallback);
177+
}
144178
}
145179

146180
resolve(firebase.instance);
@@ -160,7 +194,22 @@ firebase.addOnMessageReceivedCallback = function (callback) {
160194
}
161195

162196
firebase._receivedNotificationCallback = callback;
163-
firebase._processPendingNotifications();
197+
198+
org.nativescript.plugins.firebase.FirebasePlugin.setOnNotificationReceivedCallback(
199+
new org.nativescript.plugins.firebase.FirebasePluginListener({
200+
success: function(notification) {
201+
console.log("---------- received notification: " + notification);
202+
callback(JSON.parse(notification));
203+
}
204+
})
205+
);
206+
207+
// if the app was launched from a notification, process it now
208+
if (firebase._launchNotification !== null) {
209+
callback(firebase._launchNotification);
210+
firebase._launchNotification = null;
211+
}
212+
164213
resolve();
165214
} catch (ex) {
166215
console.log("Error in firebase.addOnMessageReceivedCallback: " + ex);
@@ -169,6 +218,30 @@ firebase.addOnMessageReceivedCallback = function (callback) {
169218
});
170219
};
171220

221+
firebase.addOnPushTokenReceivedCallback = function (callback) {
222+
return new Promise(function (resolve, reject) {
223+
try {
224+
if (typeof(com.google.firebase.messaging) === "undefined") {
225+
reject("Uncomment firebase-messaging in the plugin's include.gradle first");
226+
return;
227+
}
228+
229+
org.nativescript.plugins.firebase.FirebasePlugin.setOnPushTokenReceivedCallback(
230+
new org.nativescript.plugins.firebase.FirebasePluginListener({
231+
success: function(token) {
232+
callback(token);
233+
}
234+
})
235+
);
236+
237+
resolve();
238+
} catch (ex) {
239+
console.log("Error in firebase.addOnPushTokenReceivedCallback: " + ex);
240+
reject(ex);
241+
}
242+
});
243+
};
244+
172245
firebase.getRemoteConfigDefaults = function (properties) {
173246
var defaults = {};
174247
for (var p in properties) {
@@ -180,6 +253,13 @@ firebase.getRemoteConfigDefaults = function (properties) {
180253
return defaults;
181254
};
182255

256+
firebase._isGooglePlayServicesAvailable = function () {
257+
var context = appModule.android.foregroundActivity;
258+
var playServiceStatusSuccess = com.google.android.gms.common.ConnectionResult.SUCCESS; // 0
259+
var playServicesStatus = com.google.android.gms.common.GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
260+
return playServicesStatus === playServiceStatusSuccess;
261+
};
262+
183263
firebase.getRemoteConfig = function (arg) {
184264
return new Promise(function (resolve, reject) {
185265
try {
@@ -193,6 +273,11 @@ firebase.getRemoteConfig = function (arg) {
193273
return;
194274
}
195275

276+
if (!firebase._isGooglePlayServicesAvailable()) {
277+
reject("Google Play services is required for this feature, but not available on this device");
278+
return;
279+
}
280+
196281
// Get a Remote Config object instance
197282
firebaseRemoteConfig = com.google.firebase.remoteconfig.FirebaseRemoteConfig.getInstance();
198283

@@ -236,7 +321,6 @@ firebase.getRemoteConfig = function (arg) {
236321

237322
var onFailureListener = new com.google.android.gms.tasks.OnFailureListener({
238323
onFailure: function (exception) {
239-
console.log("--- onFailureListener: " + exception);
240324
if (exception == "com.google.firebase.remoteconfig.FirebaseRemoteConfigFetchThrottledException") {
241325
returnMethod(true);
242326
} else {
@@ -323,6 +407,11 @@ function toLoginResult(user) {
323407
firebase.login = function (arg) {
324408
return new Promise(function (resolve, reject) {
325409
try {
410+
if (!firebase._isGooglePlayServicesAvailable()) {
411+
reject("Google Play services is required for this feature, but not available on this device");
412+
return;
413+
}
414+
326415
var firebaseAuth = com.google.firebase.auth.FirebaseAuth.getInstance();
327416
var onCompleteListener = new com.google.android.gms.tasks.OnCompleteListener({
328417
onComplete: function (task) {

firebase.d.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,27 @@ declare module "nativescript-plugin-firebase" {
203203

204204
/**
205205
* The returned object in the callback handler of the addOnMessageReceivedCallback function.
206+
*
206207
* Note that any custom data you send from your server will be available as
207208
* key/value properties on the Message object.
208209
*/
209210
export interface Message {
211+
/**
212+
* Indicated whether or not the notification was received while the app was in the foreground.
213+
*/
214+
foreground: boolean;
210215
/**
211216
* The main text shown in the notificiation.
217+
* Not available on Android when the notification was received in the background.
212218
*/
213-
body: string;
219+
body?: string;
214220
/**
215221
* Optional title, shown above the body in the notification.
222+
* Not available on Android when the notification was received in the background.
216223
*/
217224
title?: string;
218225
/**
219-
* iOS badge number
226+
* iOS badge count, as sent from the server.
220227
*/
221228
badge?: number;
222229
}
@@ -226,6 +233,7 @@ declare module "nativescript-plugin-firebase" {
226233
export function logout(): Promise<any>;
227234
export function getRemoteConfig(options: GetRemoteConfigOptions): Promise<GetRemoteConfigResult>;
228235
export function addOnMessageReceivedCallback(onMessageReceived: (data: Message) => void): Promise<any>;
236+
export function addOnPushTokenReceivedCallback(onPushTokenReceived: (data: string) => void): Promise<any>;
229237
export function createUser(options: CreateUserOptions): Promise<CreateUserResult>;
230238
export function deleteUser(): Promise<any>;
231239
export function resetPassword(options: ResetPasswordOptions): Promise<any>;

firebase.ios.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ var types = require("utils/types");
44
var frame = require("ui/frame");
55

66
firebase._messagingConnected = null;
7+
firebase._pendingNotifications = [];
8+
firebase._receivedPushTokenCallback = null;
79

810
firebase._addObserver = function (eventName, callback) {
911
return NSNotificationCenter.defaultCenter().addObserverForNameObjectQueueUsingBlock(eventName, null, NSOperationQueue.mainQueue(), callback);
@@ -36,14 +38,15 @@ firebase.addAppDelegateMethods = function(appDelegate) {
3638
var userInfoJSON = firebase.toJsObject(userInfo);
3739

3840
if (application.applicationState === UIApplicationState.UIApplicationStateActive) {
39-
// foreground
4041
if (firebase._receivedNotificationCallback !== null) {
42+
userInfoJSON.foreground = true;
4143
firebase._receivedNotificationCallback(userInfoJSON);
4244
} else {
45+
userInfoJSON.foreground = false;
4346
firebase._pendingNotifications.push(userInfoJSON);
4447
}
4548
} else {
46-
// background
49+
userInfoJSON.foreground = false;
4750
firebase._pendingNotifications.push(userInfoJSON);
4851
}
4952
};
@@ -68,6 +71,23 @@ firebase.addOnMessageReceivedCallback = function (callback) {
6871
});
6972
};
7073

74+
firebase.addOnPushTokenReceivedCallback = function (callback) {
75+
return new Promise(function (resolve, reject) {
76+
try {
77+
if (typeof(FIRMessaging) === "undefined") {
78+
reject("Enable FIRMessaging in Podfile first");
79+
return;
80+
}
81+
82+
firebase._receivedPushTokenCallback = callback;
83+
resolve();
84+
} catch (ex) {
85+
console.log("Error in firebase.addOnPushTokenReceivedCallback: " + ex);
86+
reject(ex);
87+
}
88+
});
89+
};
90+
7191
firebase._processPendingNotifications = function() {
7292
if (firebase._receivedNotificationCallback !== null) {
7393
for (var p in firebase._pendingNotifications) {
@@ -252,9 +272,14 @@ firebase.init = function (arg) {
252272
// Firebase notifications (FCM)
253273
if (typeof(FIRMessaging) !== "undefined") {
254274
firebase._addObserver(kFIRInstanceIDTokenRefreshNotification, firebase._onTokenRefreshNotification);
275+
255276
if (arg.onMessageReceivedCallback !== undefined) {
256277
firebase.addOnMessageReceivedCallback(arg.onMessageReceivedCallback);
257278
}
279+
280+
if (arg.onPushTokenReceivedCallback !== undefined) {
281+
firebase.addOnPushTokenReceivedCallback(arg.onPushTokenReceivedCallback);
282+
}
258283
}
259284

260285
resolve(firebase.instance);
@@ -272,6 +297,11 @@ firebase._onTokenRefreshNotification = function (notification) {
272297
}
273298

274299
console.log("Firebase FCM token received: " + token);
300+
301+
if (firebase._receivedPushTokenCallback) {
302+
firebase._receivedPushTokenCallback(token);
303+
}
304+
275305
FIRMessaging.messaging().connectWithCompletion(function(error) {
276306
if (error !== null) {
277307
// this is not fatal at all but still would like to know how often this happens

platforms/android/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
For more sophisticated handling of Firebase Messaging we need to implement 2 services.
2+
Those services must be configured in `AndroidManifest.xml` and we need to ship 2 additional classes.
3+
4+
To make it as easy as possible for consumers of this plugin we bundle those bith in an `.aar` file.
5+
6+
Steps to update the `.aar` file:
7+
8+
* Clone this repo
9+
* Start Android Studio and pick 'import existing project' > `{this repo}/platforms/android/libraryproject`
10+
* Update `firebase/src/main/AndroidManifest.xml` as needed
11+
* Open the Gradle toolwindow
12+
* Run `firebase > Tasks > build > build`
13+
* The (release) `.aar` will be generated in `firebase/build/outputs/aar`
14+
* Copy that to the `platforms/android` folder, replacing the old `.aar`
15+
* Commit and push the changes as usual
5.35 KB
Binary file not shown.

platforms/android/include.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ repositories {
1313
}
1414

1515
dependencies {
16-
// make sure you have these versions by updating your local Android SDK's (Android Support repo and Google repo)
16+
// make sure you have these versions by updating your local Android SDK's (Android Support repo and Google repo)
1717
compile "com.google.firebase:firebase-database:9.0.2"
1818
compile "com.google.firebase:firebase-auth:9.0.2"
1919

2020
// for reading google-services.json and configuration
21-
compile "com.google.android.gms:play-services:9.0.2"
21+
compile "com.google.android.gms:play-services-base:9.0.2"
2222

2323
// Uncomment if you want to use 'Remote Config'
2424
// compile "com.google.firebase:firebase-config:9.0.2"

0 commit comments

Comments
 (0)