Skip to content
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

Form update notifications #2095

Merged
merged 23 commits into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0062bf3
Added new option in settings for updates
grzesiek2010 Mar 26, 2018
42b0c34
Removed duplicated code from FormManagementPreferences class
grzesiek2010 Mar 26, 2018
e92cc62
Added evernote job dependency
grzesiek2010 Mar 27, 2018
d81b6b0
Moved the code form DownloadFormListTask to DownloadFormListUtils in …
grzesiek2010 Mar 27, 2018
b922b9b
Implemented changes for polling a server periodically
grzesiek2010 Mar 27, 2018
4cc1264
Implemented changes in the database
grzesiek2010 Mar 27, 2018
4cea712
Polling a server
grzesiek2010 Mar 30, 2018
c893938
Cancel job after resetting settings
grzesiek2010 Apr 3, 2018
e7775d2
Update the job after scanning/reading settings
grzesiek2010 Apr 3, 2018
189a8f9
Display only updated forms after opening the list from a notification
grzesiek2010 Apr 3, 2018
1e1d5cf
Improved checking media files
grzesiek2010 Apr 5, 2018
6225eb6
Poll server after connecting to the internet if las attempt failed
grzesiek2010 Apr 5, 2018
06124b9
Catch exception taht causes problems with tests
grzesiek2010 Apr 11, 2018
f8d232f
Improved rescheduling job after reading/resetting settings - removed …
grzesiek2010 Apr 11, 2018
9f1d5a3
Removed additional manifest file downloading since in most cases the …
grzesiek2010 Apr 16, 2018
259802d
Removed unused imports
grzesiek2010 Apr 16, 2018
90afb9b
Added automatic update
grzesiek2010 Apr 27, 2018
4e5fe9e
Disable 'automatic update' option if periodic job is set to 'never'
grzesiek2010 Apr 27, 2018
91be570
Fixed bugs I introduced while working on automatic download
grzesiek2010 Apr 27, 2018
4c4583f
Improved notifications -remved not legible messages added mesagge abo…
grzesiek2010 Apr 27, 2018
a86a1a4
Improvements
grzesiek2010 May 8, 2018
4cee64d
Merge branch 'master' into form_update_notifications
grzesiek2010 May 11, 2018
235728b
Code improvements
grzesiek2010 May 11, 2018
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 collect_app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ dependencies {
exclude group: 'org.apache.httpcomponents'
}

implementation group: 'com.evernote', name: 'android-job', version: '1.2.5'
implementation group: 'commons-io', name: 'commons-io', version: '2.5'
implementation group: 'net.sf.kxml', name: 'kxml2', version: '2.3.0'
implementation group: 'net.sf.opencsv', name: 'opencsv', version: '2.3'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@

import timber.log.Timber;

import static org.odk.collect.android.utilities.DownloadFormListUtils.DL_AUTH_REQUIRED;
import static org.odk.collect.android.utilities.DownloadFormListUtils.DL_ERROR_MSG;

/**
* Responsible for displaying, adding and deleting all the valid forms in the forms directory. One
* caveat. If the server requires authentication, a dialog will pop up asking when you request the
Expand All @@ -76,6 +79,7 @@ public class FormDownloadList extends FormListActivity implements FormListDownlo
private static final int AUTH_DIALOG = 2;
private static final int CANCELLATION_DIALOG = 3;

public static final String DISPLAY_ONLY_UPDATED_FORMS = "displayOnlyUpdatedForms";
private static final String BUNDLE_SELECTED_COUNT = "selectedcount";
private static final String BUNDLE_FORM_MAP = "formmap";
private static final String DIALOG_TITLE = "dialogtitle";
Expand Down Expand Up @@ -114,6 +118,7 @@ public class FormDownloadList extends FormListActivity implements FormListDownlo
private boolean shouldExit;
private static final String SHOULD_EXIT = "shouldexit";

private boolean displayOnlyUpdatedForms;

@SuppressWarnings("unchecked")
@Override
Expand All @@ -122,6 +127,10 @@ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(getString(R.string.get_forms));

if (getIntent().getExtras() != null) {
displayOnlyUpdatedForms = (boolean) getIntent().getExtras().get(DISPLAY_ONLY_UPDATED_FORMS);
}

alertMsg = getString(R.string.please_wait);

downloadButton = findViewById(R.id.add_button);
Expand Down Expand Up @@ -579,14 +588,14 @@ public void formListDownloadingComplete(HashMap<String, FormDetails> result) {
return;
}

if (result.containsKey(DownloadFormListTask.DL_AUTH_REQUIRED)) {
if (result.containsKey(DL_AUTH_REQUIRED)) {
// need authorization
showDialog(AUTH_DIALOG);
} else if (result.containsKey(DownloadFormListTask.DL_ERROR_MSG)) {
} else if (result.containsKey(DL_ERROR_MSG)) {
// Download failed
String dialogMessage =
getString(R.string.list_failed_with_error,
result.get(DownloadFormListTask.DL_ERROR_MSG).getErrorStr());
result.get(DL_ERROR_MSG).getErrorStr());
String dialogTitle = getString(R.string.load_remote_form_error);
createAlertDialog(dialogTitle, dialogMessage, DO_NOT_EXIT);
} else {
Expand All @@ -599,28 +608,31 @@ public void formListDownloadingComplete(HashMap<String, FormDetails> result) {
for (int i = 0; i < result.size(); i++) {
String formDetailsKey = ids.get(i);
FormDetails details = formNamesAndURLs.get(formDetailsKey);
HashMap<String, String> item = new HashMap<String, String>();
item.put(FORMNAME, details.getFormName());
item.put(FORMID_DISPLAY,
((details.getFormVersion() == null) ? "" : (getString(R.string.version) + " "
+ details.getFormVersion() + " ")) + "ID: " + details.getFormID());
item.put(FORMDETAIL_KEY, formDetailsKey);
item.put(FORM_ID_KEY, details.getFormID());
item.put(FORM_VERSION_KEY, details.getFormVersion());

// Insert the new form in alphabetical order.
if (formList.size() == 0) {
formList.add(item);
} else {
int j;
for (j = 0; j < formList.size(); j++) {
HashMap<String, String> compareMe = formList.get(j);
String name = compareMe.get(FORMNAME);
if (name.compareTo(formNamesAndURLs.get(ids.get(i)).getFormName()) > 0) {
break;

if (!displayOnlyUpdatedForms || (details.isNewerFormVersionAvailable() || details.areNewerMediaFilesAvailable())) {
HashMap<String, String> item = new HashMap<String, String>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Fairly trivial point, but I've seen code analysers complain about variable assignments similar to line 613. It seems to be generally preferable to use the Interface Map<String, String> as the variable type. The rational being that you can more easily change the implemention.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, and also you can elide the second “String, String” inside the <>.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but I didn't add this. Since this pr is pretty big itself we should do such a refactoring in a separate one.

item.put(FORMNAME, details.getFormName());
item.put(FORMID_DISPLAY,
((details.getFormVersion() == null) ? "" : (getString(R.string.version) + " "
+ details.getFormVersion() + " ")) + "ID: " + details.getFormID());
item.put(FORMDETAIL_KEY, formDetailsKey);
item.put(FORM_ID_KEY, details.getFormID());
item.put(FORM_VERSION_KEY, details.getFormVersion());

// Insert the new form in alphabetical order.
if (formList.size() == 0) {
formList.add(item);
} else {
int j;
for (j = 0; j < formList.size(); j++) {
HashMap<String, String> compareMe = formList.get(j);
String name = compareMe.get(FORMNAME);
if (name.compareTo(formNamesAndURLs.get(ids.get(i)).getFormName()) > 0) {
break;
}
}
formList.add(j, item);
}
formList.add(j, item);
}
}
filteredFormList.addAll(formList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import org.odk.collect.android.preferences.PreferenceKeys;
import org.odk.collect.android.preferences.PreferencesActivity;
import org.odk.collect.android.provider.InstanceProviderAPI.InstanceColumns;
import org.odk.collect.android.tasks.ServerPollingJob;
import org.odk.collect.android.utilities.ApplicationConstants;
import org.odk.collect.android.utilities.AuthDialogUtility;
import org.odk.collect.android.utilities.PlayServicesUtil;
Expand All @@ -68,6 +69,8 @@

import timber.log.Timber;

import static org.odk.collect.android.preferences.PreferenceKeys.KEY_PERIODIC_FORM_UPDATES_CHECK;

/**
* Responsible for displaying buttons to launch the major activities. Launches
* some activities based on returns of others.
Expand Down Expand Up @@ -652,6 +655,9 @@ private boolean loadSharedPreferencesFromFile(File src) {
prefEdit.putLong(key, (Long) v);
} else if (v instanceof String) {
prefEdit.putString(key, ((String) v));
if (key.equals(KEY_PERIODIC_FORM_UPDATES_CHECK)) {
ServerPollingJob.schedulePeriodicJob((String) v);
}
}
}
prefEdit.apply();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import android.view.View;
import android.view.inputmethod.InputMethodManager;

import com.evernote.android.job.JobManager;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.Tracker;
import com.google.firebase.crash.FirebaseCrash;
Expand All @@ -52,6 +53,7 @@
import org.odk.collect.android.utilities.AuthDialogUtility;
import org.odk.collect.android.utilities.LocaleHelper;
import org.odk.collect.android.utilities.PRNGFixes;
import org.odk.collect.android.utilities.ServerPollingJobCreator;
import org.opendatakit.httpclientandroidlib.client.CookieStore;
import org.opendatakit.httpclientandroidlib.client.CredentialsProvider;
import org.opendatakit.httpclientandroidlib.client.protocol.HttpClientContext;
Expand Down Expand Up @@ -266,6 +268,10 @@ public void onCreate() {

reloadSharedPreferences();

JobManager
.create(this)
.addJobCreator(new ServerPollingJobCreator());

PRNGFixes.apply();
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
JodaTimeAndroid.init(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.JR_FORM_ID;
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.JR_VERSION;
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.LANGUAGE;
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.LAST_DETECTED_FORM_VERSION_HASH;
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.MD5_HASH;
import static org.odk.collect.android.provider.FormsProviderAPI.FormsColumns.SUBMISSION_URI;

Expand All @@ -50,7 +51,7 @@ public class FormsDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "forms.db";
public static final String FORMS_TABLE_NAME = "forms";

private static final int DATABASE_VERSION = 5;
private static final int DATABASE_VERSION = 6;

// These exist in database versions 2 and 3, but not in 4...
private static final String TEMP_FORMS_TABLE_NAME = "forms_v4";
Expand Down Expand Up @@ -79,9 +80,11 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
success &= upgradeToVersion4(db, oldVersion);
case 4:
success &= upgradeToVersion5(db);
case 5:
success &= upgradeToVersion6(db);
break;
default:
Timber.i("Unknown version " + oldVersion);
Timber.i("Unknown version %s", oldVersion);
}

if (success) {
Expand Down Expand Up @@ -276,6 +279,22 @@ private boolean upgradeToVersion5(SQLiteDatabase db) {
return success;
}

private boolean upgradeToVersion6(SQLiteDatabase db) {
boolean success = true;
try {
CustomSQLiteQueryBuilder
.begin(db)
.alter()
.table(FORMS_TABLE_NAME)
.addColumn(LAST_DETECTED_FORM_VERSION_HASH, "text")
.end();
} catch (SQLiteException e) {
Timber.e(e);
success = false;
}
return success;
}

private void createFormsTable(SQLiteDatabase db, String tableName) {
db.execSQL("CREATE TABLE " + tableName + " ("
+ _ID + " integer primary key, "
Expand All @@ -292,7 +311,8 @@ private void createFormsTable(SQLiteDatabase db, String tableName) {
+ SUBMISSION_URI + " text, "
+ BASE64_RSA_PUBLIC_KEY + " text, "
+ JRCACHE_FILE_PATH + " text not null, "
+ AUTO_SEND + " text,"
+ AUTO_DELETE + " text);");
+ AUTO_SEND + " text, "
+ AUTO_DELETE + " text, "
+ LAST_DETECTED_FORM_VERSION_HASH + " text);");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class FormDetails implements Serializable {
private String manifestUrl;
private String formID;
private String formVersion;
private String hash;
private boolean isNewerFormVersionAvailable;
private boolean areNewerMediaFilesAvailable;

Expand All @@ -33,13 +34,14 @@ public FormDetails(String error) {
}

public FormDetails(String formName, String downloadUrl, String manifestUrl, String formID,
String formVersion, boolean isNewerFormVersionAvailable,
String formVersion, String hash, boolean isNewerFormVersionAvailable,
boolean areNewerMediaFilesAvailable) {
this.formName = formName;
this.downloadUrl = downloadUrl;
this.manifestUrl = manifestUrl;
this.formID = formID;
this.formVersion = formVersion;
this.hash = hash;
this.isNewerFormVersionAvailable = isNewerFormVersionAvailable;
this.areNewerMediaFilesAvailable = areNewerMediaFilesAvailable;
}
Expand Down Expand Up @@ -68,6 +70,10 @@ public String getFormVersion() {
return formVersion;
}

public String getHash() {
return hash;
}

public boolean isNewerFormVersionAvailable() {
return isNewerFormVersionAvailable;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public final class AdminKeys {
private static final String KEY_SHOW_SPLASH_SCREEN = "show_splash_screen";
private static final String KEY_DELETE_AFTER_SEND = "delete_after_send";
private static final String KEY_INSTANCE_FORM_SYNC = "instance_form_sync";
private static final String KEY_PERIODIC_FORM_UPDATES_CHECK = "periodic_form_updates_check";
private static final String KEY_APP_LANGUAGE = "change_app_language";

private static final String KEY_AUTOSEND = "change_autosend";
Expand Down Expand Up @@ -79,6 +80,7 @@ public final class AdminKeys {
ag(KEY_SHOW_SPLASH_SCREEN, PreferenceKeys.KEY_SPLASH_PATH),
ag(KEY_DELETE_AFTER_SEND, PreferenceKeys.KEY_DELETE_AFTER_SEND),
ag(KEY_INSTANCE_FORM_SYNC, PreferenceKeys.KEY_INSTANCE_SYNC),
ag(KEY_PERIODIC_FORM_UPDATES_CHECK, PreferenceKeys.KEY_PERIODIC_FORM_UPDATES_CHECK),

ag(KEY_AUTOSEND, PreferenceKeys.KEY_AUTOSEND),

Expand Down Expand Up @@ -125,7 +127,8 @@ public final class AdminKeys {
KEY_CONSTRAINT_BEHAVIOR,
KEY_HIGH_RESOLUTION,
KEY_IMAGE_SIZE,
KEY_INSTANCE_FORM_SYNC
KEY_INSTANCE_FORM_SYNC,
KEY_PERIODIC_FORM_UPDATES_CHECK
);

static Collection<String> userInterfaceKeys = Arrays.asList(
Expand Down
Loading