diff --git a/build.gradle b/build.gradle index 034997552..95768edf3 100644 --- a/build.gradle +++ b/build.gradle @@ -2,20 +2,20 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' - classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.9.8' - classpath 'com.google.gms:google-services:4.3.5' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.2' + classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.23.4' + classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' } } allprojects { repositories { google() - jcenter() + mavenCentral() ivy { url 'http://cwe.cs.washington.edu:8082/artifactory/libs-demo/' } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d1b3a547e..a843c3c44 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip diff --git a/services_app/build.gradle b/services_app/build.gradle index 1502ffa33..d3cf80d5d 100644 --- a/services_app/build.gradle +++ b/services_app/build.gradle @@ -28,7 +28,6 @@ android { versionCode(releaseVersionCode) versionName(versionCodeName) testInstrumentationRunner(instrumentationRunner) - multiDexEnabled true } flavorDimensions "stage", "testing" @@ -119,14 +118,13 @@ dependencies { implementation fileTree(include: '*.jar', dir: 'libs') implementation 'androidx.annotation:annotation:1.2.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.preference:preference:1.1.1' - implementation 'androidx.fragment:fragment:1.3.2' - implementation 'com.google.android.material:material:1.3.0' - implementation 'com.google.firebase:firebase-analytics:18.0.2' - implementation 'com.google.firebase:firebase-crashlytics:17.4.1' + implementation 'androidx.fragment:fragment:1.3.6' + implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.firebase:firebase-analytics:19.0.2' + implementation 'com.google.firebase:firebase-crashlytics:18.2.3' - implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' implementation group: 'com.google.zxing', name: 'core', version: '3.3.0' @@ -144,8 +142,8 @@ dependencies { } // Testing-only dependencies - androidTestImplementation 'androidx.test:runner:1.3.0' - androidTestImplementation 'androidx.test:rules:1.3.0' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation fileTree(include: '*.jar', dir: 'libs') androidTestImplementation 'androidx.annotation:annotation:1.2.0' diff --git a/services_app/src/main/java/org/opendatakit/services/application/Services.java b/services_app/src/main/java/org/opendatakit/services/application/Services.java index e221b85cc..7e35e7afe 100644 --- a/services_app/src/main/java/org/opendatakit/services/application/Services.java +++ b/services_app/src/main/java/org/opendatakit/services/application/Services.java @@ -15,22 +15,16 @@ package org.opendatakit.services.application; import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.util.Log; - -import androidx.multidex.MultiDexApplication; import com.google.firebase.analytics.FirebaseAnalytics; import org.opendatakit.application.IToolAware; -import org.opendatakit.logging.WebLogger; +import org.opendatakit.application.ToolAwareApplication; import org.opendatakit.services.R; import java.lang.ref.WeakReference; -public final class Services extends MultiDexApplication implements IToolAware { +public final class Services extends ToolAwareApplication implements IToolAware { private static final String t = Services.class.getSimpleName(); @@ -50,64 +44,6 @@ public void onCreate() { analytics.logEvent(FirebaseAnalytics.Event.APP_OPEN, null); } - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - Log.i(t, "onConfigurationChanged"); - } - - @Override - public void onTerminate() { - WebLogger.closeAll(); - super.onTerminate(); - Log.i(t, "onTerminate"); - } - - /** - * The tool name is the name of the package after the org.opendatakit. prefix. - * - * @return the tool name. - */ - @Override - public String getToolName() { - String packageName = getPackageName(); - String[] parts = packageName.split("\\."); - return parts[2]; - } - - @Override - public String getVersionCodeString() { - try { - PackageInfo pinfo; - pinfo = getPackageManager().getPackageInfo(getPackageName(), 0); - int versionNumber = pinfo.versionCode; - return Integer.toString(versionNumber); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return ""; - } - } - - @Override - public String getVersionDetail() { - String versionDetail = ""; - try { - PackageInfo pinfo; - pinfo = getPackageManager().getPackageInfo(getPackageName(), 0); - String versionName = pinfo.versionName; - versionDetail = " " + versionName; - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - return versionDetail; - } - - @Override - public String getVersionedToolName() { - String versionDetail = this.getVersionDetail(); - return getString(getApkDisplayNameResourceId()) + versionDetail; - } - @Deprecated public static Context _please_dont_use_getInstance() { return singleton.get(); } diff --git a/services_app/src/main/java/org/opendatakit/services/sync/actions/activities/AbsSyncBaseActivity.java b/services_app/src/main/java/org/opendatakit/services/sync/actions/activities/AbsSyncBaseActivity.java index 44f96f388..f907fd6c3 100644 --- a/services_app/src/main/java/org/opendatakit/services/sync/actions/activities/AbsSyncBaseActivity.java +++ b/services_app/src/main/java/org/opendatakit/services/sync/actions/activities/AbsSyncBaseActivity.java @@ -40,6 +40,7 @@ import org.opendatakit.consts.IntentConsts; import org.opendatakit.fragment.AboutMenuFragment; import org.opendatakit.logging.WebLogger; +import org.opendatakit.logging.desktop.WebLoggerDesktopFactoryImpl; import org.opendatakit.properties.CommonToolProperties; import org.opendatakit.properties.PropertiesSingleton; import org.opendatakit.services.R; @@ -317,7 +318,9 @@ public static void showAuthenticationErrorDialog(final Activity activity, String builder.setMessage(message); builder.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - activity.finish(); + if (activity instanceof VerifyServerSettingsActivity){ + activity.finish(); + } dialog.dismiss(); } }); diff --git a/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessAppAndTableLevelChanges.java b/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessAppAndTableLevelChanges.java index f5724250a..6c3209e58 100644 --- a/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessAppAndTableLevelChanges.java +++ b/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessAppAndTableLevelChanges.java @@ -244,15 +244,6 @@ public List synchronizeConfigurationAndContent(boolean pushToServ return new ArrayList<>(); } - // set device properties to cause all ODK tools to re-run their initialization tasks. - sc.setAllToolsToReInitialize(); - - // Everything was successful-enough to warrant deleting any sync - // ETags from syncing to a different server. This ensures that we - // only ever have the sync etags from the current server in case - // the user is switching servers for some reason. - manifestProcessor.deleteAllSyncETagsExceptForCurrentServer(); - sc.updateNotification(SyncProgressState.STARTING, R.string.sync_retrieving_tables_list_from_server, null, 0.0, false); @@ -326,6 +317,32 @@ public List synchronizeConfigurationAndContent(boolean pushToServ return new ArrayList(); } + // Fail if local table schemaETags don't match those on the server + if (!pushToServer) { + boolean matched = doDeviceTableSchemaETagsMatchServerETags(tables, localTableIds, db); + if (!matched) { + return new ArrayList(); + } + } + + // Only initialize if app level file manifest or table level file manifest is different from + // server + boolean initializeTools = true; + if (!pushToServer) { + initializeTools = shouldInitializeAllTools(tableList, tables); + } + + if (initializeTools) { + // set device properties to cause all ODK tools to re-run their initialization tasks. + sc.setAllToolsToReInitialize(); + + // Everything was successful-enough to warrant deleting any sync + // ETags from syncing to a different server. This ensures that we + // only ever have the sync etags from the current server in case + // the user is switching servers for some reason. + manifestProcessor.deleteAllSyncETagsExceptForCurrentServer(); + } + // Figure out how many major steps there are to the sync { Set uniqueTableIds = new HashSet(); @@ -614,6 +631,63 @@ public List synchronizeConfigurationAndContent(boolean pushToServ return workingListOfTables; } + private boolean doDeviceTableSchemaETagsMatchServerETags(List tables, + List localTableIds, DbHandle db) + throws ServicesAvailabilityException { + try { + db = sc.getDatabase(); + for (TableResource table : tables) { + if (localTableIds.contains(table.getTableId())) { + TableDefinitionEntry entry = sc.getDatabaseService().getTableDefinitionEntry( + sc.getAppName(), db, table.getTableId()); + if (!table.getSchemaETag().equals(entry.getSchemaETag())) { + sc.setAppLevelSyncOutcome(SyncOutcome.TABLE_SCHEMA_COLUMN_DEFINITION_MISMATCH); + log.e(TAG, + "[synchronizeConfigurationAndContent] schemaETag on server does not match " + + "local table"); + return false; + } + } + } + } catch (Exception e) { + sc.setAppLevelSyncOutcome(sc.exceptionEquivalentOutcome(e)); + log.e(TAG, + "[synchronizeConfigurationAndContent] exception getting local table definition" + + " entry: " + e.toString()); + return false; + } finally { + if (db != null) { + sc.releaseDatabase(db); + } + } + return true; + } + + private boolean shouldInitializeAllTools(TableResourceList tableList, List tables) + throws ServicesAvailabilityException { + String appLevelManifest = null; + if (tableList != null && tableList.getAppLevelManifestETag() != null) { + appLevelManifest = tableList.getAppLevelManifestETag(); + } + + if (appLevelManifest == null) { + return true; + } else if (!appLevelManifest.equals(manifestProcessor.getManifestSyncETag(null))) { + return true; + } + + for (TableResource table : tables) { + String tableLevelManifest = table.getTableLevelManifestETag(); + if (tableLevelManifest == null) { + return true; + } else if (!tableLevelManifest.equals( + manifestProcessor.getManifestSyncETag(table.getTableId()))) { + return true; + } + } + return false; + } + /** * Synchronize the table represented by the given TableProperties with the * cloud. diff --git a/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessManifestContentAndFileChanges.java b/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessManifestContentAndFileChanges.java index 37dd388ef..59deaf5fa 100644 --- a/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessManifestContentAndFileChanges.java +++ b/services_app/src/main/java/org/opendatakit/services/sync/service/logic/ProcessManifestContentAndFileChanges.java @@ -1086,7 +1086,7 @@ private void updateFileSyncETag(URI fileDownloadUri, String tableId, long lastMo * @return * @throws ServicesAvailabilityException */ - private String getManifestSyncETag(String tableId) throws ServicesAvailabilityException { + public String getManifestSyncETag(String tableId) throws ServicesAvailabilityException { URI fileManifestUri; diff --git a/settings.gradle b/settings.gradle index dc0aff044..6907764c4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,76 +1,33 @@ -gradle.ext.gradleConfigVersion = 150 - +gradle.ext.gradleConfigVersion = 155 if (!gradle.ext.has('workspacePath')) { - - def env = System.getProperties(); - - logger.warn("services/settings.gradle System.getProperties().stringPropertyNames(): " + env - - .stringPropertyNames()); - - def path = System.getProperty('com.android.studio.gradle.project.path'); - - if (path != null) { - - logger.warn("services/settings.gradle Found value for System.getProperty('com.android" + - - ".studio.gradle.project.path')"); - - gradle.ext.workspacePath = (new File(path)).getParentFile().getAbsolutePath(); - - } else { - - logger.warn("services/settings.gradle No value found for System.getProperty('com.android" + - - ".studio.gradle.project.path')"); - - gradle.ext.workspacePath = new File("..").getAbsolutePath(); - - } - + logger.warn("rootDir: " + rootDir.getAbsolutePath()); + gradle.ext.workspacePath = rootDir.getParentFile().getAbsolutePath(); } - - logger.warn('services/settings.gradle -- gradle.ext.workspacePath: ' + gradle.ext.workspacePath) - - gradle.ext.local = gradle.ext.workspacePath + '/gradle-config/remote.gradle' - gradle.ext.remote = 'https://raw.githubusercontent.com/opendatakit/gradle-config/' + gradle.ext.gradleConfigVersion + '/remote.gradle' - gradle.ext.useLocal = true // set to false to always build against remote artifacts - if ((new File(gradle.ext.local)).exists()) { - gradle.ext.useLocalPaths = true - apply from: gradle.ext.local - } else { - gradle.ext.useLocalPaths = false - apply from: gradle.ext.remote - } settingsScripts.each { - apply from: it - } include(servicesProjectChild) - - if (libraryProjectPath.exists() && gradle.ext.useLocal) { // check if local library project is present @@ -80,4 +37,3 @@ if (libraryProjectPath.exists() && gradle.ext.useLocal) { include libraryProjectName project(libraryProjectName).setProjectDir(libraryProjectPath) // set local location } -