package org.commcare.activities; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import org.commcare.CommCareApplication; import org.commcare.android.database.user.models.FormRecord; import org.commcare.dalvik.R; import org.commcare.engine.resource.installers.SingleAppInstallation; import org.commcare.interfaces.WithUIController; import org.commcare.models.database.SqlStorage; import org.commcare.network.DataPullRequester; import org.commcare.network.LocalReferencePullResponseFactory; import org.commcare.network.mocks.LocalFilePullResponseFactory; import org.commcare.preferences.CommCareServerPreferences; import org.commcare.suite.model.OfflineUserRestore; import org.commcare.tasks.DataPullTask; import org.commcare.tasks.FormSubmissionProgressBarListener; import org.commcare.tasks.ProcessAndSendTask; import org.commcare.tasks.PullTaskResultReceiver; import org.commcare.tasks.ResultAndError; import org.commcare.utils.FormUploadResult; import org.commcare.utils.StorageUtils; import org.javarosa.core.model.User; import org.javarosa.core.reference.InvalidReferenceException; import org.javarosa.core.reference.ReferenceManager; import org.javarosa.core.services.locale.Localization; import java.io.File; import java.io.IOException; /** * Processes and submits forms and syncs data with server */ public class FormAndDataSyncer { public FormAndDataSyncer() { } /** * @return Were forms sent to the server by this method invocation? */ public boolean checkAndStartUnsentFormsTask(SyncCapableCommCareActivity activity, final boolean syncAfterwards, boolean userTriggered) { SqlStorage<FormRecord> storage = CommCareApplication.instance().getUserStorage(FormRecord.class); FormRecord[] records = StorageUtils.getUnsentRecordsForCurrentApp(storage); if (records.length > 0) { processAndSendForms(activity, records, syncAfterwards, userTriggered); return true; } else { return false; } } @SuppressLint("NewApi") public void processAndSendForms(final SyncCapableCommCareActivity activity, FormRecord[] records, final boolean syncAfterwards, final boolean userTriggered) { ProcessAndSendTask<SyncCapableCommCareActivity> processAndSendTask = new ProcessAndSendTask<SyncCapableCommCareActivity>(activity, getFormPostURL(activity), syncAfterwards) { @Override protected void deliverResult(SyncCapableCommCareActivity receiver, FormUploadResult result) { if (CommCareApplication.instance().isConsumerApp()) { // if this is a consumer app we don't want to show anything in the UI about // sending forms, or do a sync afterward return; } if (result == FormUploadResult.PROGRESS_LOGGED_OUT) { receiver.finish(); return; } if (receiver instanceof WithUIController) { ((WithUIController)receiver).getUIController().refreshView(); } int successfulSends = this.getSuccessfulSends(); if (result == FormUploadResult.FULL_SUCCESS) { String label = Localization.get("sync.success.sent.singular", new String[]{String.valueOf(successfulSends)}); if (successfulSends > 1) { label = Localization.get("sync.success.sent", new String[]{String.valueOf(successfulSends)}); } receiver.handleFormSendResult(label, true); if (syncAfterwards) { syncDataForLoggedInUser(receiver, true, userTriggered); } } else if (result == FormUploadResult.AUTH_FAILURE) { receiver.handleFormSendResult(Localization.get("sync.fail.auth.loggedin"), false); } else if (result != FormUploadResult.FAILURE) { // Tasks with failure result codes will have already created a notification receiver.handleFormSendResult(Localization.get("sync.fail.unsent"), false); } } @Override protected void deliverUpdate(SyncCapableCommCareActivity receiver, Long... update) { } @Override protected void deliverError(SyncCapableCommCareActivity receiver, Exception e) { receiver.handleFormSendResult(Localization.get("sync.fail.unsent"), false); } }; processAndSendTask.addSubmissionListener( CommCareApplication.instance().getSession().getListenerForSubmissionNotification()); if (activity.usesSubmissionProgressBar()) { processAndSendTask.addProgressBarSubmissionListener( new FormSubmissionProgressBarListener(activity)); } processAndSendTask.connect(activity); processAndSendTask.executeParallel(records); } private static String getFormPostURL(final Context context) { SharedPreferences settings = CommCareApplication.instance().getCurrentApp().getAppPreferences(); return settings.getString(CommCareServerPreferences.PREFS_SUBMISSION_URL_KEY, context.getString(R.string.PostURL)); } public void syncDataForLoggedInUser(final SyncCapableCommCareActivity activity, final boolean formsToSend, final boolean userTriggeredSync) { User u = CommCareApplication.instance().getSession().getLoggedInUser(); if (User.TYPE_DEMO.equals(u.getUserType())) { if (userTriggeredSync) { // Remind the user that there's no syncing in demo mode. if (formsToSend) { activity.handleSyncNotAttempted(Localization.get("main.sync.demo.has.forms")); } else { activity.handleSyncNotAttempted(Localization.get("main.sync.demo.no.forms")); } } return; } SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); syncData(activity, formsToSend, userTriggeredSync, prefs.getString(CommCareServerPreferences.PREFS_DATA_SERVER_KEY, activity.getString(R.string.ota_restore_url)), u.getUsername(), u.getCachedPwd(), u.getUniqueId()); } public void performOtaRestore(LoginActivity context, String username, String password) { SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); syncData(context, false, false, prefs.getString(CommCareServerPreferences.PREFS_DATA_SERVER_KEY, context.getString(R.string.ota_restore_url)), username, password, null); } public <I extends CommCareActivity & PullTaskResultReceiver> void performCustomRestoreFromFile( I context, File incomingRestoreFile) { User u = CommCareApplication.instance().getSession().getLoggedInUser(); String username = u.getUsername(); LocalFilePullResponseFactory.setRequestPayloads(new File[]{incomingRestoreFile}); syncData(context, false, false, "fake-server-that-is-never-used", username, null, "unused", LocalFilePullResponseFactory.INSTANCE, true); } public <I extends CommCareActivity & PullTaskResultReceiver> void performLocalRestore( I context, String username, String password) { try { ReferenceManager.instance().DeriveReference( SingleAppInstallation.LOCAL_RESTORE_REFERENCE).getStream(); } catch (InvalidReferenceException | IOException e) { throw new RuntimeException("Local restore file missing"); } LocalReferencePullResponseFactory.setRequestPayloads(new String[]{SingleAppInstallation.LOCAL_RESTORE_REFERENCE}); syncData(context, false, false, "fake-server-that-is-never-used", username, password, "unused", LocalReferencePullResponseFactory.INSTANCE, true); } public <I extends CommCareActivity & PullTaskResultReceiver> void performDemoUserRestore( I context, OfflineUserRestore offlineUserRestore) { String[] demoUserRestore = new String[]{offlineUserRestore.getReference()}; LocalReferencePullResponseFactory.setRequestPayloads(demoUserRestore); syncData(context, false, false, "fake-server-that-is-never-used", offlineUserRestore.getUsername(), OfflineUserRestore.DEMO_USER_PASSWORD, "demo_id", LocalReferencePullResponseFactory.INSTANCE, true); } public <I extends CommCareActivity & PullTaskResultReceiver> void syncData( final I activity, final boolean formsToSend, final boolean userTriggeredSync, String server, String username, String password, String userId) { syncData(activity, formsToSend, userTriggeredSync, server, username, password, userId, CommCareApplication.instance().getDataPullRequester(), false); } private <I extends CommCareActivity & PullTaskResultReceiver> void syncData( final I activity, final boolean formsToSend, final boolean userTriggeredSync, String server, String username, String password, String userId, DataPullRequester dataPullRequester, boolean blockRemoteKeyManagement) { DataPullTask<PullTaskResultReceiver> dataPullTask = new DataPullTask<PullTaskResultReceiver>( username, password, userId, server, activity, dataPullRequester, blockRemoteKeyManagement) { @Override protected void deliverResult(PullTaskResultReceiver receiver, ResultAndError<PullTaskResult> resultAndErrorMessage) { receiver.handlePullTaskResult(resultAndErrorMessage, userTriggeredSync, formsToSend); } @Override protected void deliverUpdate(PullTaskResultReceiver receiver, Integer... update) { receiver.handlePullTaskUpdate(update); } @Override protected void deliverError(PullTaskResultReceiver receiver, Exception e) { receiver.handlePullTaskError(); } }; dataPullTask.connect(activity); dataPullTask.executeParallel(); } }