Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sample implementation of registration tokens for FCM #1453

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
6 changes: 6 additions & 0 deletions messaging/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,14 @@ dependencies {

implementation 'com.google.firebase:firebase-installations-ktx:17.1.0'

// Used to store FCM registration tokens
implementation 'com.google.firebase:firebase-firestore-ktx:24.1.0'
andreaowu marked this conversation as resolved.
Show resolved Hide resolved

implementation 'androidx.work:work-runtime:2.7.1'

// Used for Firestore
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'

// Testing dependencies
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.google.firebase.quickstart.fcm.kotlin
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
Expand All @@ -11,11 +12,20 @@ import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.gms.tasks.OnCompleteListener
import androidx.lifecycle.lifecycleScope
import com.google.firebase.Timestamp
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.quickstart.fcm.R
import com.google.firebase.quickstart.fcm.databinding.ActivityMainBinding
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import java.util.Calendar
import java.util.Date

class MainActivity : AppCompatActivity() {

Expand Down Expand Up @@ -80,25 +90,47 @@ class MainActivity : AppCompatActivity() {
binding.logTokenButton.setOnClickListener {
// Get token
// [START log_reg_token]
Firebase.messaging.getToken().addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
}

// Get new FCM registration token
val token = task.result

lifecycleScope.launch {
val token = getAndStoreRegToken()
// Log and toast
val msg = getString(R.string.msg_token_fmt, token)
Log.d(TAG, msg)
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
})
Log.d(TAG, "on click listener: $token")
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()
}
// [END log_reg_token]
}

Toast.makeText(this, "See README for setup instructions", Toast.LENGTH_SHORT).show()
askNotificationPermission()

// In the app’s first Activity
val preferences = this.getPreferences(Context.MODE_PRIVATE)
val lastRefreshLong = preferences.getLong("lastRefreshDate", -1)
Log.d(TAG, "last refresh in preferences: $lastRefreshLong")
GlobalScope.launch {
andreaowu marked this conversation as resolved.
Show resolved Hide resolved
val document = Firebase.firestore.collection("refresh").document("refreshDate").get().await()
thatfiredev marked this conversation as resolved.
Show resolved Hide resolved
val updatedTime = (document.data!!["lastRefreshDate"] as Timestamp).seconds * 1000
Log.d(TAG, "updatedTime: $updatedTime from Firestore")
val lastRefreshDate = Date(if (lastRefreshLong == -1L) updatedTime else lastRefreshLong)
val c = Calendar.getInstance().apply {
time = lastRefreshDate
add(Calendar.DATE, 30)
}
val today = Date()
if (today.after(c.time)) {
// get token and store into Firestore (see function above)
getAndStoreRegToken()
// update device cache
Log.d(TAG, "get registration token and store")
preferences.edit().putLong("lastRefreshDate", updatedTime)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new values need to be applied to the SharedPreferences after they're edited:

Suggested change
preferences.edit().putLong("lastRefreshDate", updatedTime)
preferences.edit().putLong("lastRefreshDate", updatedTime).apply()

}
}
}

private fun askNotificationPermission() {
Expand All @@ -115,6 +147,24 @@ class MainActivity : AppCompatActivity() {
}
}

private fun getAndStoreRegToken(): String {
var token = ""
runBlocking {
andreaowu marked this conversation as resolved.
Show resolved Hide resolved
token = Firebase.messaging.token.await()
Log.d(TAG, "getRegistrationToken(): $token")
// 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)
andreaowu marked this conversation as resolved.
Show resolved Hide resolved
}
return token
}

companion object {

private const val TAG = "MainActivity"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import com.google.firebase.firestore.FieldValue
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.google.firebase.quickstart.fcm.R
Expand Down Expand Up @@ -74,7 +77,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// FCM registration token to your app server.
sendRegistrationToServer(token)
sendTokenToServer(token)
}
// [END on_new_token]

Expand Down Expand Up @@ -103,9 +106,18 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
*
* @param token The new token.
*/
private fun sendRegistrationToServer(token: String?) {
private fun sendTokenToServer(token: String?) {
// TODO: Implement this method to send token to your app server.
Log.d(TAG, "sendRegistrationTokenToServer($token)")
// 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)
}

/**
Expand Down
9 changes: 9 additions & 0 deletions messaging/functions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();
exports.scheduledFunction = functions.pubsub.schedule('0 0 1 * *').onRun((context) => {
admin.firestore().doc('refresh/refreshDate').set({ lastRefreshDate : Date.now() });
});
1 change: 1 addition & 0 deletions messaging/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true