diff --git a/messaging/app/build.gradle b/messaging/app/build.gradle index 6381aaed9..39164501d 100644 --- a/messaging/app/build.gradle +++ b/messaging/app/build.gradle @@ -11,6 +11,7 @@ android { versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true } buildTypes { release { @@ -30,8 +31,14 @@ dependencies { // for Google Analytics. This is recommended, but not required. implementation 'com.google.firebase:firebase-analytics:21.2.0' + // Used to store FCM Registration Token. + // This is recommended, but not required. + // See: https://firebase.google.com/docs/cloud-messaging/manage-tokens + implementation 'com.google.firebase:firebase-firestore-ktx:24.4.3' + implementation "com.google.android.gms:play-services-auth:20.4.1" implementation 'androidx.work:work-runtime-ktx:2.7.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' } apply plugin: 'com.google.gms.google-services' diff --git a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt index f899ee848..2bc91bc64 100644 --- a/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt +++ b/messaging/app/src/main/java/com/google/firebase/example/messaging/kotlin/MainActivity.kt @@ -1,6 +1,7 @@ package com.google.firebase.example.messaging.kotlin import android.Manifest +import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -10,11 +11,19 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope +import com.google.firebase.Timestamp import com.google.firebase.example.messaging.MainActivity +import com.google.firebase.firestore.FieldValue +import com.google.firebase.firestore.ktx.firestore import com.google.firebase.ktx.Firebase import com.google.firebase.messaging.ktx.messaging import com.google.firebase.messaging.ktx.remoteMessage +import java.util.Calendar +import java.util.Date import java.util.concurrent.atomic.AtomicInteger +import kotlinx.coroutines.launch +import kotlinx.coroutines.tasks.await class MainActivity : AppCompatActivity() { @@ -130,4 +139,20 @@ class MainActivity : AppCompatActivity() { } // [END ask_post_notifications] + // [START get_store_token] + private suspend fun getAndStoreRegToken(): String { + val token = Firebase.messaging.token.await() + // Add token and timestamp to Firestore for this user + val deviceToken = hashMapOf( + "token" to token, + "timestamp" to FieldValue.serverTimestamp(), + ) + + // Get user ID from Firebase Auth or your own server + Firebase.firestore.collection("fcmTokens").document("myuserid") + .set(deviceToken).await() + return token + } + // [END get_store_token] + } diff --git a/messaging/functions/index.js b/messaging/functions/index.js new file mode 100644 index 000000000..23f8d70dc --- /dev/null +++ b/messaging/functions/index.js @@ -0,0 +1,44 @@ +'use strict'; + +const functions = require('firebase-functions'); +const admin = require('firebase-admin'); + +admin.initializeApp(); + +const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30; // 30 days + +/** + * Scheduled function that runs once a month. It updates the last refresh date for + * tokens so that a client can refresh the token if the last time it did so was + * before the refresh date. + */ +// [START refresh_date_scheduled_function] +exports.scheduledFunction = functions.pubsub.schedule('0 0 1 * *').onRun((context) => { + admin.firestore().doc('refresh/refreshDate').set({ lastRefreshDate : Date.now() }); +}); +// [END refresh_date_scheduled_function] + +/** + * Scheduled function that runs once a day. It retrieves all stale tokens then + * unsubscribes them from 'topic1' then deletes them. + * + * Note: weather is an example topic here. It is up to the developer to unsubscribe + * all topics the token is subscribed to. + */ +// [START remove_stale_tokens] +exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => { + const staleTokensResult = await admin.firestore().collection('fcmTokens') + .where("timestamp", "<", Date.now() - EXPIRATION_TIME) + .get(); + + const staleTokens = staleTokensResult.docs.map(staleTokenDoc => staleTokenDoc.id); + + await admin.messaging().unsubscribeFromTopic(staleTokens, 'weather'); + + const deletePromises = []; + for (const staleTokenDoc of staleTokensResult.docs) { + deletePromises.push(staleTokenDoc.ref.delete()); + } + await Promise.all(deletePromises); +}); +// [END remove_stale_tokens] \ No newline at end of file