-
-
Notifications
You must be signed in to change notification settings - Fork 27
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
Referral Notifications - FCM Push Notifications #2667
Changes from 18 commits
244f34b
5fbcdc7
c833a92
c7ece13
934379d
c818d08
03840ac
eab4931
43dd926
3ddf08d
9656cdb
2c5472a
095650a
6f2eec2
6531282
57bee40
9558b83
065c2c9
a9a7d18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
package org.commcare.services; | ||
|
||
import android.app.NotificationManager; | ||
import android.app.PendingIntent; | ||
import android.content.Intent; | ||
import android.os.Build; | ||
|
||
import androidx.core.app.NotificationCompat; | ||
|
||
import com.google.firebase.messaging.FirebaseMessagingService; | ||
import com.google.firebase.messaging.RemoteMessage; | ||
|
||
import org.commcare.CommCareNoficationManager; | ||
import org.commcare.activities.DispatchActivity; | ||
import org.commcare.dalvik.R; | ||
import org.commcare.util.LogTypes; | ||
import org.commcare.utils.FirebaseMessagingUtil; | ||
import org.javarosa.core.services.Logger; | ||
import org.joda.time.DateTime; | ||
|
||
import java.util.Map; | ||
|
||
/** | ||
* This service responds to any events/messages from Firebase Cloud Messaging. The intention is to | ||
* offer an entry point for any message from FCM and trigger the necessary steps based on the action | ||
* key. | ||
*/ | ||
public class CommCareFirebaseMessagingService extends FirebaseMessagingService { | ||
|
||
private final static int FCM_NOTIFICATION = R.string.fcm_notification; | ||
private enum ActionTypes{ | ||
SYNC, | ||
INVALID | ||
} | ||
|
||
/** | ||
* Upon receiving a new message from FCM, CommCare needs to: | ||
* 1) Trigger the notification if the message contains a Notification object. Note that the | ||
* presence of a Notification object causes the onMessageReceived to not be called when the | ||
* app is in the background, which means that the data object won't be processed from here | ||
* 2) Verify if the message contains a data object and trigger the necessary steps according | ||
* to the action it carries | ||
* | ||
*/ | ||
@Override | ||
public void onMessageReceived(RemoteMessage remoteMessage) { | ||
Logger.log(LogTypes.TYPE_FCM, "Message received: " + remoteMessage.getMessageId()); | ||
Map<String, String> payloadData = remoteMessage.getData(); | ||
RemoteMessage.Notification payloadNotification = remoteMessage.getNotification(); | ||
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can a notification contains both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. My understanding is that the main difference is when the app is in the background, the SDK doesn't call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't completely understand. How do we handle the Also when
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bumping There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a bit tricky. So, when the app is in the background and the message contains a
We can have both, but in this case because we want to trigger the sync when the app is in the background too, we are having messages with only the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That means we should be handling these intents on app startup right ? Or they by default gets delivered to this messaging service when user clicks on notification and pull the app in foreground ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @shubham1g5 This is supposed to be handled on app startup but it's not yet implemented because our current implementation on HQ doesn't trigger FCM messages with both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should not we still need to handle only
Does it sound right to you ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it, thanks for confirming. |
||
|
||
if (payloadNotification != null) { | ||
showNotification(payloadNotification); | ||
} | ||
|
||
// Check if the message contains a data object, there is no further action if not | ||
if (payloadData.size() == 0){ | ||
return; | ||
} | ||
|
||
FCMMessageData fcmMessageData = new FCMMessageData(payloadData); | ||
|
||
switch(fcmMessageData.action){ | ||
case SYNC -> {} // trigger sync for fcmMessageData | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
default -> | ||
Logger.log(LogTypes.TYPE_FCM, "Invalid FCM action"); | ||
} | ||
} | ||
|
||
@Override | ||
public void onNewToken(String token) { | ||
// TODO: Remove the token from the log | ||
Logger.log(LogTypes.TYPE_FCM, "New registration token was generated"+token); | ||
FirebaseMessagingUtil.updateFCMToken(token); | ||
} | ||
|
||
|
||
/** | ||
* This method purpose is to show notifications to the user when the app is in the foreground. | ||
* When the app is in the background, FCM is responsible for notifying the user | ||
* | ||
*/ | ||
private void showNotification(RemoteMessage.Notification notification) { | ||
String notificationTitle = notification.getTitle(); | ||
String notificationText = notification.getBody(); | ||
NotificationManager mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); | ||
|
||
Intent i = new Intent(this, DispatchActivity.class); | ||
i.setAction(Intent.ACTION_MAIN); | ||
i.addCategory(Intent.CATEGORY_LAUNCHER); | ||
|
||
PendingIntent contentIntent; | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) | ||
contentIntent = PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_IMMUTABLE); | ||
else | ||
contentIntent = PendingIntent.getActivity(this, 0, i, 0); | ||
|
||
NotificationCompat.Builder fcmNotification = new NotificationCompat.Builder(this, | ||
CommCareNoficationManager.NOTIFICATION_CHANNEL_SERVER_COMMUNICATIONS_ID) | ||
.setContentTitle(notificationTitle) | ||
.setContentText(notificationText) | ||
.setContentIntent(contentIntent) | ||
.setSmallIcon(R.drawable.notification) | ||
.setPriority(NotificationCompat.PRIORITY_HIGH) | ||
.setWhen(System.currentTimeMillis()); | ||
|
||
mNM.notify(FCM_NOTIFICATION, fcmNotification.build()); | ||
} | ||
|
||
/** | ||
* This class is to facilitate handling the FCM Message Data object. It should contain all the | ||
* necessary checks and transformations | ||
*/ | ||
public class FCMMessageData { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: best to put this model in a new file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @shubham1g5 This was done on #2680 in this commit |
||
private ActionTypes action; | ||
private String username; | ||
private String domain; | ||
private DateTime creationTime; | ||
|
||
private FCMMessageData(Map<String, String> payloadData){ | ||
this.action = getActionType(payloadData.get("action")); | ||
this.username = payloadData.get("username"); | ||
this.domain = payloadData.get("domain"); | ||
this.creationTime = convertISO8601ToDateTime(payloadData.get("created_at")); | ||
} | ||
|
||
private DateTime convertISO8601ToDateTime(String timeInISO8601) { | ||
if (timeInISO8601 == null){ | ||
return null; | ||
} | ||
return new DateTime(timeInISO8601); | ||
} | ||
|
||
private ActionTypes getActionType(String action) { | ||
if (action == null) { | ||
return ActionTypes.INVALID; | ||
} | ||
|
||
switch (action.toUpperCase()) { | ||
case "SYNC" -> { | ||
return ActionTypes.SYNC; | ||
} | ||
default -> { | ||
return ActionTypes.INVALID; | ||
} | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pending todo