Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/google/ground-android int…
Browse files Browse the repository at this point in the history
…o gino-m/prototypes/json-schema
  • Loading branch information
gino-m committed Feb 24, 2024
2 parents abafd21 + ec64292 commit 04c50b9
Show file tree
Hide file tree
Showing 77 changed files with 1,179 additions and 437 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ tasks.dependencyUpdates {
ext {
androidCompileSdk = 34
androidMinSdk = 24
androidTargetSdk = 30
androidTargetSdk = 33

gmsMapsVersion = '18.1.0'
jvmToolchainVersion = 17
Expand Down
2 changes: 1 addition & 1 deletion config/detekt/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ naming:
FunctionNaming:
active: true
excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ]
functionPattern: '[a-z][a-zA-Z0-9]*'
functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
excludeClassPattern: '$^'
FunctionParameterNaming:
active: true
Expand Down
12 changes: 9 additions & 3 deletions ground/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,13 @@ android {

buildFeatures {
buildConfig true
compose true
dataBinding true
viewBinding true
}
composeOptions {
kotlinCompilerExtensionVersion "1.5.5"
}
testOptions {
unitTests {
includeAndroidResources = true
Expand Down Expand Up @@ -182,9 +186,11 @@ dependencies {

// UI widgets.
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.google.android.material:material:1.9.0'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.compose.material3:material3:1.1.0"
implementation 'androidx.compose.ui:ui:1.6.1'
implementation 'androidx.compose.compiler:compiler:1.5.9'
implementation 'androidx.compose.material3:material3-android:1.2.0'

// Google Play Services.
implementation 'com.google.android.gms:play-services-auth:20.6.0'
Expand Down Expand Up @@ -273,7 +279,7 @@ dependencies {
testImplementation 'com.google.truth:truth:1.1.3'
androidTestImplementation 'com.google.truth:truth:1.1.3'
testImplementation 'androidx.test:core:1.5.0'
testImplementation 'org.robolectric:robolectric:4.9.2'
testImplementation 'org.robolectric:robolectric:4.11.1'
testImplementation 'android.arch.core:core-testing:1.1.1'
androidTestImplementation 'android.arch.core:core-testing:1.1.1'
testImplementation 'com.jraska.livedata:testing:1.2.0'
Expand Down
15 changes: 13 additions & 2 deletions ground/src/main/java/com/google/android/ground/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ object Config {
const val MAX_MEDIA_UPLOAD_RETRY_COUNT = 5

// TODO(#1730): Make sub-paths configurable and stop hardcoding here.
fun getMogSources(url: String) =
listOf(MogSource("${url}/world-masked.tif", 0..7), MogSource("${url}/{x}/{y}.tif", 8..14))
const val DEFAULT_MOG_MIN_ZOOM = 8
const val DEFAULT_MOG_MAX_ZOOM = 14
fun getMogSources(path: String = "/offline-imagery/default/") =
listOf(
MogSource(
0 ..< DEFAULT_MOG_MIN_ZOOM,
"$path/$DEFAULT_MOG_MIN_ZOOM/overview.tif",
),
MogSource(
DEFAULT_MOG_MIN_ZOOM..DEFAULT_MOG_MAX_ZOOM,
"$path/$DEFAULT_MOG_MIN_ZOOM/{x}/{y}.tif",
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ class GroundApplication : MultiDexApplication(), Configuration.Provider {
@Inject lateinit var crashReportingTree: CrashReportingTree
@Inject lateinit var workerFactory: HiltWorkerFactory

init {
Timber.plant(if (isReleaseBuild()) crashReportingTree else Timber.DebugTree())
}

private fun isReleaseBuild(): Boolean = BuildConfig.BUILD_TYPE.contentEquals("release")

override fun onCreate() {
super.onCreate()
Timber.plant(if (isReleaseBuild()) crashReportingTree else Timber.DebugTree())
if (!isReleaseBuild()) {
Timber.d("DEBUG build config active; enabling debug tooling")

Expand Down
12 changes: 6 additions & 6 deletions ground/src/main/java/com/google/android/ground/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package com.google.android.ground

import android.app.ProgressDialog
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import androidx.core.view.WindowInsetsCompat
Expand All @@ -33,7 +33,7 @@ import com.google.android.ground.ui.common.NavigateTo
import com.google.android.ground.ui.common.NavigateUp
import com.google.android.ground.ui.common.NavigationRequest
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.common.ProgressDialogs
import com.google.android.ground.ui.common.ProgressDialogs.modalSpinner
import com.google.android.ground.ui.common.ViewModelFactory
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
Expand All @@ -55,7 +55,7 @@ class MainActivity : AbstractActivity() {
private lateinit var viewModel: MainViewModel
private lateinit var navHostFragment: NavHostFragment

private var signInProgressDialog: ProgressDialog? = null
private var signInProgressDialog: AlertDialog? = null

override fun onCreate(savedInstanceState: Bundle?) {
// Make sure this is before calling super.onCreate()
Expand Down Expand Up @@ -146,14 +146,14 @@ class MainActivity : AbstractActivity() {

private fun showSignInDialog() {
if (signInProgressDialog == null) {
signInProgressDialog = ProgressDialogs.modalSpinner(this, R.string.signing_in)
signInProgressDialog = modalSpinner(this, layoutInflater, R.string.signing_in)
}
signInProgressDialog!!.show()
signInProgressDialog?.show()
}

private fun dismissSignInDialog() {
if (signInProgressDialog != null) {
signInProgressDialog!!.dismiss()
signInProgressDialog?.dismiss()
signInProgressDialog = null
}
}
Expand Down
13 changes: 11 additions & 2 deletions ground/src/main/java/com/google/android/ground/model/job/Job.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,24 @@ data class Job(
UNKNOWN
}

class TaskNotFoundException(taskId: String) : Throwable(message = "unknown task $taskId")

val canDataCollectorsAddLois: Boolean
get() = strategy != DataCollectionStrategy.PREDEFINED

val tasksSorted: List<Task>
get() = tasks.values.sortedBy { it.index }

fun getTask(id: String): Task = tasks[id] ?: error("Unknown task id $id")
// TODO(#2216): Consider using nulls to indicate absence of value here instead of throwing
// an exception.
fun getTask(id: String): Task = tasks[id] ?: throw TaskNotFoundException(id)

fun getAddLoiTask(): Task? = tasks.values.firstOrNull { it.isAddLoiTask }
/** Job must contain at-most 1 `AddLoiTask`. */
fun getAddLoiTask(): Task? =
tasks.values
.filter { it.isAddLoiTask }
.apply { check(size <= 1) { "Expected 0 or 1, found $size AddLoiTasks" } }
.firstOrNull()

/** Returns true if the job has one or more tasks. */
fun hasTasks() = tasks.values.isNotEmpty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ class LocalValueStore @Inject constructor(private val preferences: SharedPrefere
_offlineImageryEnabled.value = value
}

/** Whether to display instructions when loading draw area task. */
var drawAreaInstructionsShown: Boolean
get() = allowThreadDiskReads { preferences.getBoolean(DRAW_AREA_INSTRUCTIONS_SHOWN, false) }
set(value) = allowThreadDiskReads {
preferences.edit().putBoolean(DRAW_AREA_INSTRUCTIONS_SHOWN, value).apply()
}

/** Removes all values stored in the local store. */
fun clear() = allowThreadDiskWrites { preferences.edit().clear().apply() }

Expand Down Expand Up @@ -120,5 +127,6 @@ class LocalValueStore @Inject constructor(private val preferences: SharedPrefere
const val TOS_ACCEPTED = "tos_accepted"
const val LOCATION_LOCK_ENABLED = "location_lock_enabled"
const val OFFLINE_MAP_IMAGERY = "offline_map_imagery"
const val DRAW_AREA_INSTRUCTIONS_SHOWN = "draw_area_instructions_shown"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ object SubmissionDataConverter {
ValueJsonConverter.toResponse(task, jsonObject[taskId])?.let { map[taskId] = it }
} catch (e: LocalDataConsistencyException) {
Timber.d("Bad submission data in local db: ${e.message}")
} catch (e: Job.TaskNotFoundException) {
Timber.d(e, "Ignoring data for unknown task")
}
}
} catch (e: JSONException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ object SubmissionDeltasConverter {
Timber.d("Bad submission value in local db: " + e.message)
} catch (e: DataStoreException) {
Timber.d("Bad submission value in local db: " + e.message)
} catch (e: Job.TaskNotFoundException) {
Timber.d(e, "Ignoring delta for unknown task")
}
}
} catch (e: JSONException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,23 @@ internal object SubmissionConverter {
}

private fun putValue(taskId: String, job: Job, obj: Any, data: MutableMap<String, Value>) {
val task = job.getTask(taskId)
when (task.type) {
Task.Type.PHOTO,
Task.Type.TEXT -> putTextResponse(taskId, obj, data)
Task.Type.MULTIPLE_CHOICE -> putMultipleChoiceResponse(taskId, task.multipleChoice, obj, data)
Task.Type.NUMBER -> putNumberResponse(taskId, obj, data)
Task.Type.DATE -> putDateResponse(taskId, obj, data)
Task.Type.TIME -> putTimeResponse(taskId, obj, data)
Task.Type.DROP_PIN -> putDropPinTaskResult(taskId, obj, data)
Task.Type.DRAW_AREA -> putDrawAreaTaskResult(taskId, obj, data)
Task.Type.CAPTURE_LOCATION -> putCaptureLocationResult(taskId, obj, data)
else -> throw DataStoreException("Unknown type " + task.type)
try {
val task = job.getTask(taskId)
when (task.type) {
Task.Type.PHOTO,
Task.Type.TEXT -> putTextResponse(taskId, obj, data)
Task.Type.MULTIPLE_CHOICE ->
putMultipleChoiceResponse(taskId, task.multipleChoice, obj, data)
Task.Type.NUMBER -> putNumberResponse(taskId, obj, data)
Task.Type.DATE -> putDateResponse(taskId, obj, data)
Task.Type.TIME -> putTimeResponse(taskId, obj, data)
Task.Type.DROP_PIN -> putDropPinTaskResult(taskId, obj, data)
Task.Type.DRAW_AREA -> putDrawAreaTaskResult(taskId, obj, data)
Task.Type.CAPTURE_LOCATION -> putCaptureLocationResult(taskId, obj, data)
else -> throw DataStoreException("Unknown type " + task.type)
}
} catch (e: Job.TaskNotFoundException) {
Timber.d(e, "cannot put value for unknown task")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,9 @@ import javax.inject.Inject
/** Enqueues media upload work to be performed in the background. */
class MediaUploadWorkManager
@Inject
constructor(
private val workManager: WorkManager,
private val localValueStore: LocalValueStore,
) : SyncService() {
override val workerClass: Class<MediaUploadWorker>
get() = MediaUploadWorker::class.java
constructor(private val workManager: WorkManager, private val localValueStore: LocalValueStore) {

override fun preferredNetworkType(): NetworkType =
private fun preferredNetworkType(): NetworkType =
if (localValueStore.shouldUploadMediaOverUnmeteredConnectionOnly()) NetworkType.UNMETERED
else NetworkType.CONNECTED

Expand All @@ -45,10 +40,15 @@ constructor(
*/
fun enqueueSyncWorker(locationOfInterestId: String) {
val workInputData = createInputData(locationOfInterestId)
val request =
WorkRequestBuilder()
.setWorkerClass(MediaUploadWorker::class.java)
.setNetworkType(preferredNetworkType())
.buildWorkerRequest(workInputData)
workManager.enqueueUniqueWork(
MediaUploadWorker::class.java.name,
ExistingWorkPolicy.APPEND_OR_REPLACE,
buildWorkerRequest(workInputData)
request,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ import com.google.android.ground.persistence.sync.LocalMutationSyncWorker.Compan
import javax.inject.Inject

/** Enqueues data sync work to be done in the background. */
class MutationSyncWorkManager @Inject constructor(private val workManager: WorkManager) :
SyncService() {
override val workerClass: Class<LocalMutationSyncWorker>
get() = LocalMutationSyncWorker::class.java
class MutationSyncWorkManager @Inject constructor(private val workManager: WorkManager) {

/**
* Enqueues a worker that sends changes made locally to the remote data store once a network
Expand All @@ -38,10 +35,14 @@ class MutationSyncWorkManager @Inject constructor(private val workManager: WorkM
// implementation and avoids race conditions in the rare event the worker finishes just when new
// mutations are added to the db.
val inputData = createInputData(locationOfInterestId)
val request =
WorkRequestBuilder()
.setWorkerClass(LocalMutationSyncWorker::class.java)
.buildWorkerRequest(inputData)
workManager.enqueueUniqueWork(
LocalMutationSyncWorker::class.java.name,
ExistingWorkPolicy.APPEND,
buildWorkerRequest(inputData)
request,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@ package com.google.android.ground.persistence.sync

import androidx.work.ExistingWorkPolicy
import androidx.work.WorkManager
import java.util.*
import java.util.UUID
import javax.inject.Inject
import timber.log.Timber

/** Service responsible for enqueuing survey and LOI updates from remote server. */
class SurveySyncService @Inject constructor(private val workManager: WorkManager) : SyncService() {

override val workerClass: Class<SurveySyncWorker>
get() = SurveySyncWorker::class.java
class SurveySyncService @Inject constructor(private val workManager: WorkManager) {

/**
* Enqueues a worker that fetches the latest survey and LOIs from the remote survey and updates
Expand All @@ -35,12 +32,15 @@ class SurveySyncService @Inject constructor(private val workManager: WorkManager
*/
fun enqueueSync(surveyId: String): UUID {
val inputData = SurveySyncWorker.createInputData(surveyId)
val request = buildWorkerRequest(inputData)
val request =
WorkRequestBuilder()
.setWorkerClass(SurveySyncWorker::class.java)
.buildWorkerRequest(inputData)
workManager
.enqueueUniqueWork(
"${SurveySyncWorker::class.java}#${surveyId}",
ExistingWorkPolicy.APPEND,
request
request,
)
.result
.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.work.CoroutineWorker
import androidx.work.Data
import androidx.work.WorkerParameters
import com.google.android.ground.domain.usecases.survey.SyncSurveyUseCase
import com.google.android.ground.persistence.sync.SyncService.Companion.DEFAULT_MAX_RETRY_ATTEMPTS
import com.google.android.ground.persistence.sync.WorkRequestBuilder.Companion.DEFAULT_MAX_RETRY_ATTEMPTS
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
Expand Down
Loading

0 comments on commit 04c50b9

Please sign in to comment.