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
1 change: 1 addition & 0 deletions app/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@
<string name="change_settings">Change Settings</string>
<string name="connect_message_you">Tú</string>
<string name="connect_message_them">Ellos</string>
<string name="fcm_sync_failed_body_text">Haga clic aquí para saber más</string>
<string name="connect_progress_warning_max_reached_single">Has alcanzado el número máximo de visitas. No podrás progresar en trabajos adicionales.</string>
<string name="connect_progress_warning_daily_max_reached_single">Has alcanzado el número máximo de visitas de hoy. Es posible que no obtengas progreso por trabajo adicional.</string>
<string name="connect_progress_warning_max_reached_multi">Has alcanzado el número máximo de visitas para %s.</string>
Expand Down
1 change: 1 addition & 0 deletions app/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ License.
<string name="connect_send_message_btn_desc">Appuyez ici pour envoyer un message</string>
<string name="connect_message_you">Toi</string>
<string name="connect_message_them">Eux</string>
<string name="fcm_sync_failed_body_text">Cliquez ici pour en savoir plus</string>
<string name="connect_corrupt_job_text">Impossible de charger cette opportunité</string>
<string name="personalid_login_via_connect">Prêt à vous connecter !\n</string>
<string name="personalid_failed_to_login_with_connectid">Nous rencontrons une erreur irrécupérable lors de la connexion avec votre identifiant personnel. Veuillez vous connecter avec votre mot de passe ou restaurer votre identifiant personnel.</string>
Expand Down
2 changes: 1 addition & 1 deletion app/res/values-hi/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ License.
<string name="connect_expired">समाप्त</string>
<string name="connect_job_tile_daily_limit_description">दैनिक सीमा पूरी हो गई। फॉर्म जमा करने पर कोई भुगतान नहीं।</string>
<string name="connect_recovery_button_phone">जारी रखें</string>

<string name="fcm_sync_failed_body_text">अधिक जानकारी के लिए यहां क्लिक करें</string>
<string name="connect_verify_phone_resend_code">कोड पुनः भेजें</string>
<string name="connect_verify_skip_phone_number">मैं देख रहा हूं कि आप एक डेमो उपयोगकर्ता हैं, इसलिए हम OTP को छोड़ देंगे</string>
<string name="connect_messaging_title">मैसेजिंग</string>
Expand Down
1 change: 1 addition & 0 deletions app/res/values-lt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<string name="nav_drawer_close">Chiudi il menu di navigazione</string>
<string name="connect_message_you">Voi</string>
<string name="connect_message_them">Loro</string>
<string name="fcm_sync_failed_body_text">Norėdami sužinoti daugiau, spustelėkite čia</string>
<string name="connect_fetch_learning_progress_error">Nepavyko gauti mokymosi eigos</string>
<string name="connect_fetch_delivery_progress_error">Nepavyko gauti pristatymo eigos</string>
</resources>
1 change: 1 addition & 0 deletions app/res/values-no/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
<string name="nav_drawer_close">Lukk navigasjonsmenyen</string>
<string name="connect_message_you">Du</string>
<string name="connect_message_them">Dem</string>
<string name="fcm_sync_failed_body_text">Klikk her for å vite mer</string>
<string name="connect_fetch_learning_progress_error">Kunne ikke hente læringsfremdriften</string>
<string name="connect_fetch_delivery_progress_error">Kunne ikke hente leveringsfremdriften</string>
</resources>
1 change: 1 addition & 0 deletions app/res/values-pt/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@
<string name="connect_send_message_btn_desc">Clique aqui para enviar mensagem</string>
<string name="connect_message_you">Você</string>
<string name="connect_message_them">Eles</string>
<string name="fcm_sync_failed_body_text">Clique aqui para saber mais</string>
<string name="connect_corrupt_job_text">Falha ao carregar esta oportunidade</string>
<string name="connect_results_summary_pending">Pendente</string>
<string name="connect_results_summary_rejected">Rejeitado</string>
Expand Down
1 change: 1 addition & 0 deletions app/res/values-sw/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@
<string name="connect_send_message_btn_desc">Bonyeza hapa kutuma ujumbe</string>
<string name="connect_message_you">Wewe</string>
<string name="connect_message_them">Wao</string>
<string name="fcm_sync_failed_body_text">Bofya hapa kujua zaidi</string>
<string name="connect_corrupt_job_text">Imeshindwa kupakia fursa hii</string>
<string name="personalid_login_via_connect">Tayari kuingia!\n</string>
<string name="personalid_failed_to_login_with_connectid">Tunakabiliwa na hitilafu isiyoweza kurekebishwa wakati wa kuunganisha kuingia kwa kutumia Kitambulisho chako cha Kibinafsi. Tafadhali ingia na nenosiri lako au urejeshe kuingia kwa Kitambulisho chako cha Kibinafsi</string>
Expand Down
1 change: 1 addition & 0 deletions app/res/values-ti/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@
<string name="connect_send_message_btn_desc">መልእኽቲ ንምልኣኽ ኣብዚ ጠውቑ</string>
<string name="connect_message_you">ንስኻ</string>
<string name="connect_message_them">ንሶም</string>
<string name="fcm_sync_failed_body_text">ንዝያዳ ሓበሬታ ኣብዚ ጠውቑ</string>
<string name="connect_corrupt_job_text">ነዚ ዕድል ክጽዕን ኣይከኣለን</string>
<string name="connect_results_summary_pending">ኣብ ከይዲ</string>
<string name="connect_results_summary_rejected">ተነጺጉ</string>
Expand Down
1 change: 1 addition & 0 deletions app/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@
<string name="connect_messaging_pn_wrong_channel">Sorry, something went wrong, showing all available messaging channels</string>
<string name="connect_message_you">You</string>
<string name="connect_message_them">Them</string>
<string name="fcm_sync_failed_body_text">Click here to know more</string>

<string name="connect_last_update">Updated: %s</string>
<string name="connect_title">Connect</string>
Expand Down
1 change: 0 additions & 1 deletion app/src/org/commcare/activities/CommCareSetupActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,6 @@ public void onFailure(@NonNull PersonalIdOrConnectApiErrorCodes errorCode, @Null

@Override
public void onSuccess(ConnectOpportunitiesResponseModel data) {
ConnectJobUtils.storeJobs(activity, data.getValidJobs(), true);
boolean connectAccess = !data.getValidJobs().isEmpty() || !data.getCorruptJobs().isEmpty();
String toastMessage = getString(R.string.setup_refresh_opportunities_no_jobs);
if(connectAccess){
Expand Down
20 changes: 20 additions & 0 deletions app/src/org/commcare/connect/ConnectJobHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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!!)
}
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.applicationContext, user)
}
🤖 Prompt for AI Agents
In app/src/org/commcare/connect/ConnectJobHelper.kt around lines 138 to 153,
replace the user!! non‑null assertion with a null check: retrieve the user, if
null call listener.connectActivityComplete(false) (and optionally log) and
return early; otherwise call getConnectOpportunities using
context.applicationContext (not the incoming Context) to avoid leaking an
Activity. Ensure on null you do not attempt the API call and that the listener
is always notified.



}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ abstract class ConnectApiHandler<T> : BaseApiHandler<T>() {
fun getConnectOpportunities(context: Context, user: ConnectUserRecord) {
ApiConnect.getConnectOpportunities(
context, user, createCallback(
ConnectOpportunitiesParser<T>()
ConnectOpportunitiesParser<T>(),
context
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package org.commcare.connect.network.connect.parser

import android.content.Context
import org.commcare.android.database.connect.models.ConnectJobRecord
import org.commcare.connect.database.ConnectJobUtils
import org.commcare.connect.network.base.BaseApiResponseParser
import org.commcare.connect.network.connect.models.ConnectOpportunitiesResponseModel
import org.commcare.google.services.analytics.FirebaseAnalyticsUtil
import org.commcare.models.connect.ConnectLoginJobListModel
import org.javarosa.core.io.StreamsUtil
import org.javarosa.core.services.Logger
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import java.io.InputStream

class ConnectOpportunitiesParser<T>() : BaseApiResponseParser<T> {
Expand All @@ -34,15 +36,22 @@ class ConnectOpportunitiesParser<T>() : BaseApiResponseParser<T> {
handleCorruptJob(obj, corruptJobs)
}
}

val newJobs = ConnectJobUtils.storeJobs(anyInputObject as Context, jobs, true)
reportApiCall(true, jobs.size, newJobs)
}
}
} catch (e: JSONException) {
reportApiCall(false, 0, 0)
throw RuntimeException(e)
}

return ConnectOpportunitiesResponseModel(jobs, corruptJobs) as T

}


private fun reportApiCall(success: Boolean, totalJobs: Int, newJobs: Int) {
FirebaseAnalyticsUtil.reportCccApiJobs(success, totalJobs, newJobs)
}

private fun handleCorruptJob(obj: JSONObject?,corruptJobs: ArrayList<ConnectLoginJobListModel>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,23 +90,16 @@ public void onFailure(@NonNull PersonalIdOrConnectApiErrorCodes errorCode, @Null

@Override
public void onSuccess(ConnectOpportunitiesResponseModel data) {
int totalJobs = data.getValidJobs().size();
int newJobs = ConnectJobUtils.storeJobs(getContext(), data.getValidJobs(), true);
corruptJobs = data.getCorruptJobs();
setJobListData(data.getValidJobs());
reportApiCall(true, totalJobs, newJobs);
}
}.getConnectOpportunities(requireContext(), user);
}

private void navigateFailure() {
reportApiCall(false, 0, 0);
setJobListData(ConnectJobUtils.getCompositeJobs(getActivity(), ConnectJobRecord.STATUS_ALL_JOBS, null));
}

private void reportApiCall(boolean success, int totalJobs, int newJobs) {
FirebaseAnalyticsUtil.reportCccApiJobs(success, totalJobs, newJobs);
}

private void initRecyclerView() {
binding.connectNoJobsText.setVisibility(corruptJobs.isEmpty() && jobList.isEmpty() ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ public void onFailure(@NonNull PersonalIdOrConnectApiErrorCodes errorCode, @andr
@Override
public void onSuccess(ConnectOpportunitiesResponseModel data) {
if (!data.getValidJobs().isEmpty()) {
ConnectJobUtils.storeJobs(requireContext(), data.getValidJobs(), true);
ConnectUserDatabaseUtil.turnOnConnectAccess(requireContext());
}
setFragmentRedirection();
Expand Down
183 changes: 183 additions & 0 deletions app/src/org/commcare/pn/workermanager/PNApiSyncWorkerManager.kt
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 {
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)
}

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

}


}
8 changes: 8 additions & 0 deletions app/src/org/commcare/pn/workers/PNApiResponseStatus.kt
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
)
Loading
Loading