Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
27 changes: 23 additions & 4 deletions app/src/org/commcare/CommCareNoficationManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public class CommCareNoficationManager {
public static final String NOTIFICATION_CHANNEL_ERRORS_ID = "notification-channel-errors";
public static final String NOTIFICATION_CHANNEL_USER_SESSION_ID = "notification-channel-user-session";
public static final String NOTIFICATION_CHANNEL_SERVER_COMMUNICATIONS_ID = "notification-channel-server-communications";
public static final String NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID = "notification-channel-push-notifications";
public static final String OLD_NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID = "notification-channel-push-notifications";
public static final String NOTIFICATION_CHANNEL_GENERAL_PUSH_NOTIFICATIONS_ID = "notification-channel-general-push-notifications";
public static final String NOTIFICATION_CHANNEL_MESSAGING_ID = "notification-channel-messaging";

/**
Expand Down Expand Up @@ -191,15 +192,33 @@ public void createNotificationChannels() {
R.string.notification_channel_server_communication_description,
NotificationManager.IMPORTANCE_LOW);

createNotificationChannel(NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID,
deleteIfOldPushNotificationExists();
createNotificationChannel(NOTIFICATION_CHANNEL_GENERAL_PUSH_NOTIFICATIONS_ID,
R.string.notification_channel_push_notfications_title,
R.string.notification_channel_push_notfications_description,
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager.IMPORTANCE_HIGH);

createNotificationChannel(NOTIFICATION_CHANNEL_MESSAGING_ID,
R.string.notification_channel_messaging_title,
R.string.notification_channel_messaging_description,
NotificationManager.IMPORTANCE_MAX);
NotificationManager.IMPORTANCE_HIGH);
}

/**
* The importance level cannot be changed once a channel already exists,
* so delete this channel and create another with a different channel ID.
* Reason: Programmatically changing the importance level doesn't work,
* even by deleting and recreating it with the same ID.
* Importance level is only modifiable before the channel is submitted to
* NotificationManager. createNotificationChannel(NotificationChannel).
*/
@TargetApi(Build.VERSION_CODES.O)
private void deleteIfOldPushNotificationExists(){
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
NotificationChannel oldPushNotificationChannel = notificationManager.getNotificationChannel(OLD_NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID);
if(oldPushNotificationChannel!=null){
notificationManager.deleteNotificationChannel(OLD_NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID);
}
}

@TargetApi(Build.VERSION_CODES.O)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import kotlinx.coroutines.launch
import org.commcare.android.database.connect.models.PushNotificationRecord
import org.commcare.connect.database.NotificationRecordDatabaseHelper
import org.commcare.pn.workermanager.NotificationsSyncWorkerManager
import org.commcare.utils.PushNotificationApiHelper
import org.commcare.utils.PushNotificationApiHelper.retrieveLatestPushNotifications

class PushNotificationViewModel(
Expand Down Expand Up @@ -51,10 +52,10 @@ class PushNotificationViewModel(
.onSuccess {
val currentNotifications = _allNotifications.value.orEmpty()
NotificationsSyncWorkerManager(
application,
it,
false,
).startPNApiSync()
context = application,
showNotification = false,
syncNotification = false,
).startSyncWorkers(PushNotificationApiHelper.convertPNRecordsToPayload(it))
val updatedNotifications =
(it + currentNotifications)
.distinctBy { it.notificationId }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import com.google.gson.Gson
import org.commcare.android.database.connect.models.PushNotificationRecord
import org.commcare.connect.ConnectConstants.CCC_DEST_DELIVERY_PROGRESS
import org.commcare.connect.ConnectConstants.CCC_DEST_LEARN_PROGRESS
import org.commcare.connect.ConnectConstants.CCC_DEST_OPPORTUNITY_SUMMARY_PAGE
Expand All @@ -26,7 +25,6 @@ import org.commcare.pn.workers.NotificationsSyncWorker.Companion.ACTION
import org.commcare.pn.workers.NotificationsSyncWorker.Companion.NOTIFICATION_PAYLOAD
import org.commcare.pn.workers.NotificationsSyncWorker.Companion.SyncAction
import org.commcare.utils.FirebaseMessagingUtil.cccCheckPassed
import org.commcare.utils.PushNotificationApiHelper.convertPNRecordsToPayload
import java.util.concurrent.TimeUnit

/**
Expand Down Expand Up @@ -112,91 +110,103 @@ class NotificationsSyncWorkerManager(
delay: Long = 0,
) {
val notificationSyncWorkerManager =
NotificationsSyncWorkerManager(context, null, false, true)
notificationSyncWorkerManager.startPersonalIdNotificationsWorker(
emptyMap(),
false,
delay,
)
NotificationsSyncWorkerManager(
context,
showNotification = false,
syncNotification = true,
)
notificationSyncWorkerManager.startPersonalIdNotificationsWorker(emptyMap(), false, delay)
}
}

lateinit var notificationsPayload: ArrayList<Map<String, String>>

private var showNotification = false
private var syncNotification = false

var signaling = false
private var isNotificationSyncScheduled = false

/**
* This can receive the push notification data payload from FCM and notification API.
* @param context - android context
* @param showNotification - decide whether to show a notification or not after sync is successful.
* @param syncNotification - decide to pull / sync any pending notifications or not.
*/
constructor(
context: Context,
notificationsPayload: ArrayList<Map<String, String>>,
showNotification: Boolean,
syncNotification: Boolean = false,
syncNotification: Boolean,
) : this(context) {
this.notificationsPayload = notificationsPayload
this.showNotification = showNotification
this.syncNotification = syncNotification
}

constructor(
context: Context,
pnsRecords: List<PushNotificationRecord>?,
showNotification: Boolean,
syncNotification: Boolean = false,
) : this(context) {
this.notificationsPayload = convertPNRecordsToPayload(pnsRecords)
this.showNotification = showNotification
this.syncNotification = syncNotification
}

/**
* This method will start Api sync for received PNs either through FCM or notification API
* @return whether a sync was scheduled as part of this call
*/
fun startPNApiSync(): Boolean = startSyncWorker()

private fun startSyncWorker(): Boolean {
var isNotificationSyncScheduled = false
for (notificationPayload in notificationsPayload) {
private fun parseAndStartNotificationWorker(notificationPayload: Map<String, String>?): Boolean {
var notificationHandled = false
notificationPayload?.let {
when (notificationPayload[REDIRECT_ACTION]) {
CCC_MESSAGE -> {
startPersonalIdNotificationsWorker(notificationPayload)
notificationHandled = true
isNotificationSyncScheduled = true
}

CCC_DEST_PAYMENTS -> {
startOpportunitiesSyncWorker(notificationPayload)
startDeliverySyncWorker(notificationPayload)
notificationHandled = true
}

CCC_PAYMENT_INFO_CONFIRMATION -> {
startOpportunitiesSyncWorker(notificationPayload)
notificationHandled = true
}

CCC_DEST_OPPORTUNITY_SUMMARY_PAGE -> {
startOpportunitiesSyncWorker(notificationPayload)
notificationHandled = true
}

CCC_DEST_LEARN_PROGRESS -> {
startOpportunitiesSyncWorker(notificationPayload)
startLearningSyncWorker(notificationPayload)
notificationHandled = true
}

CCC_DEST_DELIVERY_PROGRESS -> {
startOpportunitiesSyncWorker(notificationPayload)
startDeliverySyncWorker(notificationPayload)
notificationHandled = true
}
}
}
return notificationHandled
}

/**
* This method will start sync for received payload from FCM
* @return whether the notification was handled in this call
*/
fun startSyncWorker(notificationPayload: Map<String, String>?): Boolean {
val notificationHandled = parseAndStartNotificationWorker(notificationPayload)
syncNotificationIfRequired()
return notificationHandled
}

/**
* This method will start sync for received payloads from notification API
*/
fun startSyncWorkers(notificationsPayload: ArrayList<Map<String, String>>?) {
notificationsPayload?.let {
for (notificationPayload in notificationsPayload) {
parseAndStartNotificationWorker(notificationPayload)
}
}
syncNotificationIfRequired()
}

// we want to get info on pending notifications irrespective of whether there are notification related FCMs or not
private fun syncNotificationIfRequired() {
if (syncNotification && !isNotificationSyncScheduled) {
// we want to get info on pending notifications irrespective of whether there are notification related FCMs or not
startPersonalIdNotificationsWorker(emptyMap(), false)
}
return signaling
}

private fun startPersonalIdNotificationsWorker(
Expand Down Expand Up @@ -281,6 +291,5 @@ class NotificationsSyncWorkerManager(
ExistingWorkPolicy.KEEP,
syncWorkRequest,
)
signaling = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,8 @@ public void onMessageReceived(RemoteMessage remoteMessage) {
}

private Boolean startSyncForNotification(RemoteMessage remoteMessage) {
ArrayList<Map<String, String>> pns = new ArrayList<>();
pns.add(remoteMessage.getData());
NotificationsSyncWorkerManager notificationsSyncWorkerManager = new NotificationsSyncWorkerManager(
getApplicationContext(), pns, true, true);
return notificationsSyncWorkerManager.startPNApiSync();
return new NotificationsSyncWorkerManager(getApplicationContext(), true, true).
startSyncWorker(remoteMessage.getData());
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions app/src/org/commcare/services/FCMMessageData.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public FCMMessageData(Map<String, String> payloadData) {
notificationTitle = payloadData.get(NOTIFICATION_TITLE);
notificationText = payloadData.get(NOTIFICATION_BODY);
action = payloadData.get(REDIRECT_ACTION);
priority = NotificationCompat.PRIORITY_HIGH;
notificationChannel = CommCareNoficationManager.NOTIFICATION_CHANNEL_PUSH_NOTIFICATIONS_ID;
priority = NotificationCompat.PRIORITY_MAX;
notificationChannel = CommCareNoficationManager.NOTIFICATION_CHANNEL_GENERAL_PUSH_NOTIFICATIONS_ID;
smallIcon = R.drawable.commcare_actionbar_logo;
}

Expand Down
1 change: 0 additions & 1 deletion app/src/org/commcare/utils/FirebaseMessagingUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,6 @@ private static Intent handleGeneralApplicationPushNotification(Context context,
private static Intent handleCCCMessageChannelPushNotification(Context context, FCMMessageData fcmMessageData, boolean showNotification) {
Intent intent = null;
fcmMessageData.setNotificationChannel(CommCareNoficationManager.NOTIFICATION_CHANNEL_MESSAGING_ID);
fcmMessageData.setPriority(NotificationCompat.PRIORITY_MAX);
fcmMessageData.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_connect_message_large));

boolean isMessage = fcmMessageData.getPayloadData().containsKey(ConnectMessagingMessageRecord.META_MESSAGE_ID);
Expand Down
4 changes: 4 additions & 0 deletions app/src/org/commcare/utils/PushNotificationApiHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ object PushNotificationApiHelper {
context: Context,
savedNotificationIds: List<String>,
): Boolean {
// don't call server unnecessarily if nothing to update
if (savedNotificationIds.isEmpty()) {
return true
}
val user = ConnectUserDatabaseUtil.getUser(context)
return suspendCoroutine { continuation ->
object : PersonalIdApiHandler<Boolean>() {
Expand Down