-
-
Notifications
You must be signed in to change notification settings - Fork 45
Signalising approach in FCM #3347
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
Changes from all commits
57ab684
4e1c04a
841b7fb
99486bd
144c193
7a291fa
c23f8f4
b43756a
42da91f
fd8faef
35b4e89
39b647f
df72156
9e30796
7e80f0b
a48066c
37f1b36
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,6 +8,7 @@ import org.commcare.android.database.connect.models.ConnectJobRecord | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.database.ConnectJobUtils | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.database.ConnectUserDatabaseUtil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.network.connect.ConnectApiHandler | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.network.connect.models.ConnectOpportunitiesResponseModel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.network.connect.models.DeliveryAppProgressResponseModel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.network.connect.models.LearningAppProgressResponseModel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.commcare.connect.network.connectId.PersonalIdApiErrorHandler | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -150,4 +151,23 @@ object ConnectJobHelper { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }.setPaymentConfirmation(context, user, payment.paymentId, confirmed) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun retrieveOpportunities(context: Context, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listener: ConnectActivityCompleteListener){ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| val user = ConnectUserDatabaseUtil.getUser(context) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| object : ConnectApiHandler<ConnectOpportunitiesResponseModel?>() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun onFailure( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorCode: PersonalIdOrConnectApiErrorCodes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t: Throwable? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listener.connectActivityComplete(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| override fun onSuccess(data: ConnectOpportunitiesResponseModel?) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| listener.connectActivityComplete(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }.getConnectOpportunities(context, user!!) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+155
to
+170
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. Handle null user instead of non‑null assertion user!! can crash. Fail gracefully and notify listener. Apply this diff: - fun retrieveOpportunities(context: Context,
- listener: ConnectActivityCompleteListener){
- val user = ConnectUserDatabaseUtil.getUser(context)
+ fun retrieveOpportunities(
+ context: Context,
+ listener: ConnectActivityCompleteListener
+ ){
+ val user = ConnectUserDatabaseUtil.getUser(context)
+ if (user == null) {
+ listener.connectActivityComplete(false)
+ return
+ }
object : ConnectApiHandler<ConnectOpportunitiesResponseModel?>() {
override fun onFailure(
errorCode: PersonalIdOrConnectApiErrorCodes,
t: Throwable?
) {
listener.connectActivityComplete(false)
}
override fun onSuccess(data: ConnectOpportunitiesResponseModel?) {
listener.connectActivityComplete(true)
}
- }.getConnectOpportunities(context, user!!)
+ }.getConnectOpportunities(context.applicationContext, user)
}Also prefer passing applicationContext to avoid leaking an Activity. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| package org.commcare.pn.workermanager | ||
|
|
||
| import android.content.Context | ||
| import androidx.work.Constraints | ||
| import androidx.work.Data | ||
| import androidx.work.ExistingWorkPolicy | ||
| import androidx.work.NetworkType | ||
| import androidx.work.OneTimeWorkRequestBuilder | ||
| import androidx.work.WorkInfo | ||
| import androidx.work.WorkManager | ||
| import androidx.work.WorkRequest | ||
| import com.google.gson.Gson | ||
| import com.google.gson.reflect.TypeToken | ||
| import kotlinx.coroutines.CoroutineScope | ||
| import kotlinx.coroutines.Dispatchers | ||
| import kotlinx.coroutines.flow.first | ||
| import kotlinx.coroutines.launch | ||
| 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 | ||
| import org.commcare.connect.ConnectConstants.CCC_DEST_PAYMENTS | ||
| import org.commcare.connect.ConnectConstants.CCC_MESSAGE | ||
| import org.commcare.connect.ConnectConstants.CCC_PAYMENT_INFO_CONFIRMATION | ||
| import org.commcare.connect.ConnectConstants.OPPORTUNITY_ID | ||
| import org.commcare.connect.ConnectConstants.REDIRECT_ACTION | ||
| import org.commcare.dalvik.R | ||
| import org.commcare.pn.workers.PNApiSyncWorker | ||
| import org.commcare.pn.workers.PNApiSyncWorker.Companion.ACTION | ||
| import org.commcare.pn.workers.PNApiSyncWorker.Companion.PN_DATA | ||
| import org.commcare.pn.workers.PNApiSyncWorker.Companion.SYNC_ACTION | ||
| import org.commcare.services.FCMMessageData.NOTIFICATION_BODY | ||
| import org.commcare.utils.FirebaseMessagingUtil | ||
| import org.commcare.utils.FirebaseMessagingUtil.cccCheckPassed | ||
| import org.javarosa.core.services.Logger | ||
| import java.util.concurrent.TimeUnit | ||
| import java.util.concurrent.atomic.AtomicInteger | ||
|
|
||
| /** | ||
| * This class is responsible for allocating the work request for each type of push notification | ||
| */ | ||
| class PNApiSyncWorkerManager(val context: Context) { | ||
|
|
||
|
|
||
| companion object{ | ||
| val PN_SYNC_BACKOFF_DELAY_IN_MILLIS: Long = 3 * 60 * 1000L // min 3 minutes | ||
|
|
||
| val SYNC_TYPE_STRING = "SYNC_TYPE_STRING" | ||
|
|
||
| } | ||
|
|
||
| enum class SYNC_TYPE{ | ||
| FCM, | ||
| NOTIFICATION_API | ||
| } | ||
|
|
||
| lateinit var pns : ArrayList<Map<String,String>> | ||
|
|
||
| lateinit var syncType: SYNC_TYPE | ||
|
|
||
| var signaling = false | ||
|
|
||
| /** | ||
| * This can receive the push notification data payload from FCM and notification API. | ||
| */ | ||
| constructor(context: Context, pns : ArrayList<Map<String,String>>, syncType: SYNC_TYPE):this(context){ | ||
| this.pns = pns | ||
| this.syncType = syncType | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * This method will start Api sync for received PNs either through FCM or notification API | ||
| * @return true if any signalising PN API sync is required | ||
| */ | ||
| fun startPNApiSync() : Boolean { | ||
shubham1g5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return createSyncWorkerRequest() | ||
| } | ||
|
|
||
| private fun createSyncWorkerRequest() : Boolean { | ||
| for (pn in pns) { | ||
|
|
||
| when (pn[REDIRECT_ACTION]) { | ||
|
|
||
| CCC_MESSAGE -> { | ||
| createPersonalIdMessagingSyncWorkRequest(pn) | ||
| } | ||
|
|
||
| CCC_DEST_PAYMENTS -> { | ||
| createOpportunitiesSyncWorkRequest(pn) | ||
| createDeliverySyncWorkRequest(pn) | ||
shubham1g5 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| CCC_PAYMENT_INFO_CONFIRMATION -> { | ||
| createOpportunitiesSyncWorkRequest(pn) | ||
| } | ||
|
|
||
|
|
||
| CCC_DEST_OPPORTUNITY_SUMMARY_PAGE -> { | ||
| createOpportunitiesSyncWorkRequest(pn) | ||
| } | ||
|
|
||
|
|
||
| CCC_DEST_LEARN_PROGRESS -> { | ||
| createOpportunitiesSyncWorkRequest(pn) | ||
| createLearningSyncWorkRequest(pn) | ||
| } | ||
|
|
||
| CCC_DEST_DELIVERY_PROGRESS -> { | ||
| createOpportunitiesSyncWorkRequest(pn) | ||
| createDeliverySyncWorkRequest(pn) | ||
| } | ||
|
|
||
| } | ||
| } | ||
| return signaling | ||
| } | ||
|
|
||
|
|
||
| private fun createPersonalIdMessagingSyncWorkRequest(pn:Map<String,String>){ | ||
| if(cccCheckPassed(context)) { | ||
| startWorkRequest( | ||
| pn, | ||
| SYNC_ACTION.SYNC_PERSONALID_MESSAGING, | ||
| SYNC_ACTION.SYNC_PERSONALID_MESSAGING.toString() | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| private fun createLearningSyncWorkRequest(pn:Map<String,String>){ | ||
| if(pn.containsKey(OPPORTUNITY_ID) && cccCheckPassed(context)){ | ||
| val opportunityId = pn.get(OPPORTUNITY_ID) | ||
| startWorkRequest(pn, SYNC_ACTION.SYNC_LEARNING_PROGRESS,SYNC_ACTION.SYNC_LEARNING_PROGRESS.toString()+"-${opportunityId}") | ||
| } | ||
| } | ||
|
|
||
| private fun createDeliverySyncWorkRequest(pn:Map<String,String>){ | ||
| if(pn.containsKey(OPPORTUNITY_ID) && cccCheckPassed(context)){ | ||
| val opportunityId = pn.get(OPPORTUNITY_ID) | ||
| startWorkRequest(pn, SYNC_ACTION.SYNC_DELIVERY_PROGRESS,SYNC_ACTION.SYNC_DELIVERY_PROGRESS.toString()+"-${opportunityId}") | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private fun createOpportunitiesSyncWorkRequest(pn:Map<String,String>){ | ||
| if(cccCheckPassed(context)) { | ||
| startWorkRequest( | ||
| pn, | ||
| SYNC_ACTION.SYNC_OPPORTUNITY, | ||
| SYNC_ACTION.SYNC_OPPORTUNITY.toString() | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| fun startWorkRequest(pn: Map<String,String>,action: SYNC_ACTION,uniqueWorkName:String) { | ||
| val pnJsonString = Gson().toJson(pn) | ||
| val syncActionString = Gson().toJson(action) | ||
| val syncTypeString = Gson().toJson(syncType) | ||
| val inputData = Data.Builder() | ||
| .putString(PN_DATA, pnJsonString) | ||
| .putString(ACTION,syncActionString) | ||
| .putString(PNApiSyncWorker.SYNC_TYPE,syncTypeString) | ||
| .build() | ||
|
|
||
| val workRequest = OneTimeWorkRequestBuilder<PNApiSyncWorker>() | ||
| .setInputData(inputData) | ||
| .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()) | ||
| .setBackoffCriteria( | ||
| androidx.work.BackoffPolicy.EXPONENTIAL, | ||
| PN_SYNC_BACKOFF_DELAY_IN_MILLIS, | ||
| TimeUnit.MILLISECONDS | ||
| ).build() | ||
|
|
||
| WorkManager.getInstance(context).enqueueUniqueWork( | ||
| uniqueWorkName, | ||
| ExistingWorkPolicy.KEEP, | ||
| workRequest | ||
| ) | ||
| signaling=true | ||
|
|
||
| } | ||
|
|
||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package org.commcare.pn.workers | ||
|
|
||
| import org.commcare.connect.network.base.BaseApiHandler.PersonalIdOrConnectApiErrorCodes | ||
|
|
||
| data class PNApiResponseStatus( | ||
| val success: Boolean, | ||
| val retry: Boolean | ||
| ) |
Uh oh!
There was an error while loading. Please reload this page.