-
-
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 - Background Sync #2680
Conversation
@avazirna Thanks for Pring this. Some initial feedback without going in code -
what does post background sync phase mean here ? Is this when the sync actually gets applied to change the case data or something else.
Does the next manual sync restores the skipped data to the correct state ? Asking because HQ thinks it already send it to mobile so it might skip it on next manual sync. Othwerise I agree that we should have a API flag to skip this data as it doesn't make sense to download data if we are not going to use it. |
Thanks for the notes @shubham1g5
Parking here a few notes from our chat, I'm going to focus on merging the Push notifications PR and then change the base of this PR.
The post background sync phase is basically around triggering the necessary UI updates depending on the activity that is on the foreground, i.e. at this point the sync is finished and the user sandbox has been updated
Yes, only the background sync skips these elements. |
@damagatchi retest this please |
Looks like there is already a flag that FP uses called |
This includes Logouts, Syncs and Form Entries
6da99e5
to
f2d12d0
Compare
f2d12d0
to
1194304
Compare
@@ -130,6 +130,7 @@ public abstract class CommCareActivity<R> extends AppCompatActivity | |||
private boolean isMainScreenBlocked; | |||
|
|||
DataSyncCompleteBroadcastReceiver dataSyncCompleteBroadcastReceiver = new DataSyncCompleteBroadcastReceiver(); | |||
private boolean dataSyncCompleteBroadcastReceiverRegistered = false; |
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.
- I don't think this flag is needed.
- Only activities that use this broadcast should register this broadcast instead of registering it for every
CommCareActivity
we can instead do -
if (isBackgroundSyncEnabled() and shouldListenToSyncComplete()) {
dataSyncCompleteBroadcastReceiver = new DataSyncCompleteBroadcastReceiver()
registerReceiver(dataSyncCompleteBroadcastReceiver, new IntentFilter(COMMCARE_DATA_UPDATE_ACTION));
}
// Activities that needs to refresh after sync should override this to return true
public boolean shouldListenToSyncComplete() {
return false;
}
and
if(dataSyncCompleteBroadcastReceiver!=null){
unregisterReceiver(dataSyncCompleteBroadcastReceiver);
}
@@ -565,30 +583,28 @@ public static void markRawMediaCleanUpComplete() { | |||
.edit().putBoolean(RAW_MEDIA_CLEANUP_COMPLETE, true).apply(); | |||
} | |||
|
|||
public static boolean isPendingSyncRequestFromServer() { | |||
public static boolean isPendingSyncRequestFromServerForUser() { |
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.
- this method should ideally take username as a method parameter so that we don't cause session exceptions in here.
- Confirming that
loggedusername
is not the full username like -[email protected]
?
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.
- This is to check if there is a pending sync for the current logged in user, it's triggered right after a successful login. I can rename it to make this more clear.
- Confirmed.
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.
I understand, although my suggestion is to avoid hidden session exception if in future we call this method from other places. Supplying username
to this method will ensure that caller of this method will handle implications of session not being available at that time.
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.
That's a good point
return; | ||
} | ||
// Attempt to trigger the sync if the user is currently in a sync safe activity | ||
if (isCurrentActivitySyncSafe()){ |
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.
this check actually needs to happen for restore and not form submission.
} | ||
|
||
private boolean isCurrentActivitySyncSafe() { | ||
return !syncUnsafeActivities.contains(CommCareApplication.currentActivityName); |
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.
Instead of setting the currentActivityName
in CommCareApplication
, you can directly have a variable to set backgroundSyncSafe
that you can update from FormEntryActivity
. This way every activity is deciding whether it can handle background sync instead of having to maintain a centralised list of classes in this class.
|
||
private static final String ENABLE_ANDROID_WINDOW_SECURE_FLAG = "cc-enable-android-window-secure-flag"; | ||
|
||
private static final String PENDING_SYNC_REQUEST_FROM_SERVER_TIME = "pending-sync-request-from-server-time"; |
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.
unused.
@@ -96,11 +100,22 @@ public class HiddenPreferences { | |||
private static final long NO_OF_HOURS_TO_WAIT_TO_RESUME_BACKGROUND_WORK = 36; | |||
|
|||
// This is to be used by CommCareFirebaseMessagingService to schedule a sync after the next Login | |||
public final static String PENDING_SYNC_REQUEST_FROM_SERVER = "pending-sync-request-from-server"; | |||
public final static String PENDING_SYNC_REQUEST_FROM_SERVER = "pending-sync-request-from-server-"; |
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.
nit: can just call it this BACKGROUND_SYNC_PENDING
and accordingly rename the related methods that access the preference.
@Override | ||
public void alertPendingSync(FCMMessageData fcmMessageData) { | ||
if (!HiddenPreferences.isPostFormSubmissionSyncNeeded()) { | ||
HiddenPreferences.setPostFormSubmissionSyncNeeded(true); |
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.
Wondering whether we need a separate preference to track this like why can't we use the PENDING_SYNC_REQUEST_FROM_SERVER
here as well ?
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.
Yeah, I thought about this too, the thing is that the now BACKGROUND_SYNC_PENDING
shared preference is concatenated with the username, to ensure that we trigger the sync for the right user. Consolidating both would mean to make sure that this continues to work on devices shared by different users.
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.
Consolidating both would mean to make sure that this continues to work on devices shared by different users.
That's a good thing right ?
@@ -168,6 +171,19 @@ public abstract class HomeScreenBaseActivity<T> extends SyncCapableCommCareActiv | |||
protected boolean showCommCareUpdateMenu = false; | |||
private static final int MAX_CC_UPDATE_CANCELLATION = 3; | |||
|
|||
// This is to trigger a background sync after a form submission, | |||
public static boolean shouldTriggerBackgroundSync = true; |
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.
should be private boolean shouldTriggerBackgroundSync
@@ -1249,9 +1299,15 @@ public void onResumeSessionSafe() { | |||
attemptDispatchHomeScreen(); | |||
} | |||
|
|||
// In case a Sync was blocked because of a form entry, trigger now if it's safe | |||
if (HiddenPreferences.isPostFormSubmissionSyncNeeded() && shouldTriggerBackgroundSync) { |
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.
Won't shouldTriggerBackgroundSync
will always be false
here when coming back from From Entry ? If so, how does the sync gets initiated after form submission ?
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.
@shubham1g5 Not necessarily, when we open a form onActivityResultSessionSafe
from HomeScreenBaseActivity
in called, this then triggers the logic to start a new Form Entry session, and here we set shouldTriggerBackgroundSync
to false
, so by the time onResumeSessionSafe
is called, shouldTriggerBackgroundSync
is false
and the sync is not triggered. Also, during onResumeSessionSafe
, shouldTriggerBackgroundSync
is reset to true
, which means that when onResumeSessionSafe
is called again, the sync can be triggered (if necessary), unless a new Form Entry Session is starts.
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.
which means that when onResumeSessionSafe is called again, the sync can be triggered (if necessary), unless a new Form Entry Session is starts.
But that means that sync won't get triggered immediately after user completes the form but only when user comes back to home screen one more time.
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.
@shubham1g5 I think we are supposed to go back to HomeScreenBaseActivity
after a form submission to determine the next step. Besides, we start a FormEntry activity for result in HomeScreenBaseActivity
. Are there other scenarios that I'm missing here?
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.
I am considering the scenario where a user go through this navigation -
- Home Screen -> Start -> Module -> Form Entry
- Sync request from server
- Form Entry Complete
- Home Screen (no eof navigation) but no sync
- User goes out and back into the home screen -> Sync gets triggered.
Is my interpretaion of sync behaviour in step 4 and 5 right based on the current code ?
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.
Why wouldn't there be a sync in number 4? After a form submission, we would navigate through HomeSreenBaseActivity
and shouldTriggerBackgroundSync
would be true
, so a sync would be triggered
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.
I see, My understanding is that HomeSreenBaseActivity
doesn't reinitialise when onActivityResult
gets called on the activity as there might already be a live instance of that activity in the background. But I might be wrong here.
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.
Yeah, I think so too, but why does it need to be reinitialized for the sync to occur?
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.
shouldTriggerBackgroundSync
is set to false
in startFormEntry
and will remain false
without HomeSreenBaseActivity
getting reinitialised after step 3 above.
import org.commcare.activities.HomeScreenBaseActivity; | ||
import org.commcare.models.AndroidSessionWrapper; | ||
import org.commcare.session.CommCareSession; | ||
import org.commcare.session.SessionDescriptorUtil; | ||
import org.commcare.session.SessionFrame; | ||
|
||
public class DataSyncCompleteBroadcastReceiver extends BroadcastReceiver { | ||
|
||
@Override | ||
public void onReceive(Context context, Intent intent) { |
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.
this whole class can be simplified to -
@Override
public void onReceive(Context context, Intent intent) {
AndroidSessionWrapper asw = CommCareApplication.instance().getCurrentSessionWrapper();
Activity activity = (Activity)context;
asw.cleanVolatiles();
Intent returnIntent = new Intent();
setSelectedEntity(activity, returnIntent);
activity.setResult(RESULT_RESTART, returnIntent);
activity.finish();
}
private void setSelectedEntity(Activity activity, Intent returnIntent) {
if (activity instanceof EntityDetailActivity &&
activity.getIntent().hasExtra(SessionFrame.STATE_DATUM_VAL)) {
String selectedEntity = activity.getIntent().getStringExtra(SessionFrame.STATE_DATUM_VAL);
returnIntent.putExtra(EXTRA_ENTITY_KEY, selectedEntity);
}
}
And then you can set the selectedEntityPostSync
in onActivityResult
of HomeScreenBaseActivity
and make it a private String selectedEntityPostSync
variable of activity. public static
variables in an activity should be avoided and they are mostly should only be used for constants.
@@ -196,7 +196,7 @@ public class CommCareApplication extends MultiDexApplication { | |||
private boolean invalidateCacheOnRestore; | |||
private CommCareNoficationManager noficationManager; | |||
|
|||
public static String currentActivityName; | |||
public static boolean backgroundSyncSafe; |
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.
this doesn't need to be static
, you can set it as CommCareApplication.instance().setIsBackgroundSyncSafe(false)
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.
true, on it
@@ -586,18 +584,18 @@ public static void markRawMediaCleanUpComplete() { | |||
public static boolean isPendingSyncRequestFromServerForUser() { | |||
String loggedUsername = CommCareApplication.instance().getSession().getLoggedInUser().getUsername(); | |||
return PreferenceManager.getDefaultSharedPreferences(CommCareApplication.instance()) | |||
.contains(PENDING_SYNC_REQUEST_FROM_SERVER + loggedUsername + "@"+ getUserDomainWithoutServerUrl()); | |||
.contains(BACKGROUND_SYNC_PENDING + loggedUsername + "@"+ getUserDomainWithoutServerUrl()); | |||
} | |||
public static void setPendingSyncRequestFromServerForUser(FCMMessageData fcmMessageData) { |
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.
nit: can rename all methods for BACKGROUND_SYNC_PENDING
as well - setBackgroundSyncPending()
, isBackgroundSyncPending()
and clearBackgroundSyncPending()
@Override | ||
public void alertPendingSync(FCMMessageData fcmMessageData) { | ||
if (!HiddenPreferences.isPostFormSubmissionSyncNeeded()) { | ||
HiddenPreferences.setPostFormSubmissionSyncNeeded(true); |
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.
Consolidating both would mean to make sure that this continues to work on devices shared by different users.
That's a good thing right ?
try { | ||
return new DateTime(timeInISO8601); | ||
} catch (Exception e) { | ||
return new DateTime(); |
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.
we should log the exception to sumo here so that we get to know about these issues
@@ -1249,9 +1299,15 @@ public void onResumeSessionSafe() { | |||
attemptDispatchHomeScreen(); | |||
} | |||
|
|||
// In case a Sync was blocked because of a form entry, trigger now if it's safe | |||
if (HiddenPreferences.isPostFormSubmissionSyncNeeded() && shouldTriggerBackgroundSync) { |
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.
shouldTriggerBackgroundSync
is set to false
in startFormEntry
and will remain false
without HomeSreenBaseActivity
getting reinitialised after step 3 above.
dd63a18
to
be12087
Compare
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.
The new changes looks good, we need to resolve conflicts and ensure build passes on it before merge.
ad754e0
to
65cc28e
Compare
Summary
This is the second PR related with the implementation of the Referral Notification feature. This part is responsible for the background sync. Background syncs are triggered after a successful delivery of a FCM message and when there is an active user session. The main goal of the sync is to allow the user access to newly created or updated referral cases without any action from the user.
Product Description
The way the background sync was planned is to be the least disruptive to the users, so there are aspects before, during and after a sync.
post_sync_entityselect_case_update.mov
post_sync_entitydetail_case_update.mov
post_sync_entitydetail_case_closed.mov
Safety Assurance
Notes for reviewers
fixture
andschema
blocks of the restore file. I wonder if it makes sense to have a flag preventing these from being in the restore file.Cross-request: dimagi/commcare-core#1310