Skip to content

Commit 1e4d075

Browse files
authored
add snippets to demonstrate fcm registration tokens best practices (firebase#426)
Copying the snippets from firebase/quickstart-android#1453 to this repo so that they can be added to the docs. Having these snippets on the other repo makes it harder to maintain the samples. Having them on this repo instead should be safer.
1 parent 067d83a commit 1e4d075

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

messaging/app/build.gradle

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ android {
1111
versionCode 1
1212
versionName "1.0"
1313
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14+
multiDexEnabled true
1415
}
1516
buildTypes {
1617
release {
@@ -30,8 +31,14 @@ dependencies {
3031
// for Google Analytics. This is recommended, but not required.
3132
implementation 'com.google.firebase:firebase-analytics:21.2.0'
3233

34+
// Used to store FCM Registration Token.
35+
// This is recommended, but not required.
36+
// See: https://firebase.google.com/docs/cloud-messaging/manage-tokens
37+
implementation 'com.google.firebase:firebase-firestore-ktx:24.4.3'
38+
3339
implementation "com.google.android.gms:play-services-auth:20.4.1"
3440
implementation 'androidx.work:work-runtime-ktx:2.7.1'
41+
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
3542
}
3643

3744
apply plugin: 'com.google.gms.google-services'

messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt

+25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.google.firebase.example.messaging.kotlin
22

33
import android.Manifest
4+
import android.content.Context
45
import android.content.pm.PackageManager
56
import android.os.Build
67
import android.os.Bundle
@@ -10,11 +11,19 @@ import androidx.activity.result.contract.ActivityResultContracts
1011
import androidx.annotation.RequiresApi
1112
import androidx.appcompat.app.AppCompatActivity
1213
import androidx.core.content.ContextCompat
14+
import androidx.lifecycle.lifecycleScope
15+
import com.google.firebase.Timestamp
1316
import com.google.firebase.example.messaging.MainActivity
17+
import com.google.firebase.firestore.FieldValue
18+
import com.google.firebase.firestore.ktx.firestore
1419
import com.google.firebase.ktx.Firebase
1520
import com.google.firebase.messaging.ktx.messaging
1621
import com.google.firebase.messaging.ktx.remoteMessage
22+
import java.util.Calendar
23+
import java.util.Date
1724
import java.util.concurrent.atomic.AtomicInteger
25+
import kotlinx.coroutines.launch
26+
import kotlinx.coroutines.tasks.await
1827

1928
class MainActivity : AppCompatActivity() {
2029

@@ -130,4 +139,20 @@ class MainActivity : AppCompatActivity() {
130139
}
131140
// [END ask_post_notifications]
132141

142+
// [START get_store_token]
143+
private suspend fun getAndStoreRegToken(): String {
144+
val token = Firebase.messaging.token.await()
145+
// Add token and timestamp to Firestore for this user
146+
val deviceToken = hashMapOf(
147+
"token" to token,
148+
"timestamp" to FieldValue.serverTimestamp(),
149+
)
150+
151+
// Get user ID from Firebase Auth or your own server
152+
Firebase.firestore.collection("fcmTokens").document("myuserid")
153+
.set(deviceToken).await()
154+
return token
155+
}
156+
// [END get_store_token]
157+
133158
}

messaging/functions/index.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const functions = require('firebase-functions');
4+
const admin = require('firebase-admin');
5+
6+
admin.initializeApp();
7+
8+
const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days
9+
10+
/**
11+
* Scheduled function that runs once a month. It updates the last refresh date for
12+
* tokens so that a client can refresh the token if the last time it did so was
13+
* before the refresh date.
14+
*/
15+
// [START refresh_date_scheduled_function]
16+
exports.scheduledFunction = functions.pubsub.schedule('0 0 1 * *').onRun((context) => {
17+
admin.firestore().doc('refresh/refreshDate').set({ lastRefreshDate : Date.now() });
18+
});
19+
// [END refresh_date_scheduled_function]
20+
21+
/**
22+
* Scheduled function that runs once a day. It retrieves all stale tokens then
23+
* unsubscribes them from 'topic1' then deletes them.
24+
*
25+
* Note: weather is an example topic here. It is up to the developer to unsubscribe
26+
* all topics the token is subscribed to.
27+
*/
28+
// [START remove_stale_tokens]
29+
exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
30+
const staleTokensResult = await admin.firestore().collection('fcmTokens')
31+
.where("timestamp", "<", Date.now() - EXPIRATION_TIME)
32+
.get();
33+
34+
const staleTokens = staleTokensResult.docs.map(staleTokenDoc => staleTokenDoc.id);
35+
36+
await admin.messaging().unsubscribeFromTopic(staleTokens, 'weather');
37+
38+
const deletePromises = [];
39+
for (const staleTokenDoc of staleTokensResult.docs) {
40+
deletePromises.push(staleTokenDoc.ref.delete());
41+
}
42+
await Promise.all(deletePromises);
43+
});
44+
// [END remove_stale_tokens]

0 commit comments

Comments
 (0)