Skip to content

Commit

Permalink
Merge branch 'master' into sufy/1995/conditional-branches
Browse files Browse the repository at this point in the history
  • Loading branch information
shobhitagarwal1612 authored Mar 1, 2024
2 parents aae0036 + 544841d commit 18f8b41
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 22 deletions.
10 changes: 5 additions & 5 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ steps:
args:
- '-c'
- |
./gradlew -PdisablePreDex assembleDevStaging assembleDevStagingUnitTest -PtestBuildType=staging
./gradlew -PdisablePreDex assembleDevStaging assembleDevStagingUnitTest -PtestBuildType=staging --no-daemon
# TODO(#1547): Re-enable once instrumentation tests are fixed.
# if [[ "${_PUSH_TO_MASTER}" ]]; then
Expand All @@ -100,11 +100,11 @@ steps:
args:
- '-c'
- |
./gradlew -PdisablePreDex checkCode 2> check-logs.txt || echo "fail" > build-status.txt
./gradlew -PdisablePreDex checkCode --no-daemon 2> check-logs.txt || echo "fail" > build-status.txt
cat check-logs.txt
if [[ "${_PUSH_TO_MASTER}" ]]; then
./gradlew -PdisablePreDex dependencyUpdates
./gradlew -PdisablePreDex dependencyUpdates --no-daemon
fi
- name: 'gcr.io/$PROJECT_ID/android:34'
Expand All @@ -114,11 +114,11 @@ steps:
args:
- '-c'
- |
./gradlew -PdisablePreDex testDevStagingUnitTest 2> unit-test-logs.txt || echo "fail" > build-status.txt
./gradlew -PdisablePreDex testDevStagingUnitTest --no-daemon 2> unit-test-logs.txt || echo "fail" > build-status.txt
cat unit-test-logs.txt
# TODO: Add a check for _PUSH_TO_MASTER
./gradlew jacocoTestStagingUnitTestReport
./gradlew jacocoTestStagingUnitTestReport --no-daemon
- name: 'gcr.io/$PROJECT_ID/android:34'
id: &authenticate_gcloud 'Authorize gcloud'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,69 @@
package com.google.android.ground.ui.common

import android.app.Application
import android.view.View
import android.widget.Toast
import androidx.annotation.StringRes
import com.google.android.ground.R
import com.google.android.material.snackbar.Snackbar
import javax.inject.Inject
import javax.inject.Singleton

/** Displays short-lived messages such as toasts that are shown over other UI elements. */
@Suppress("UseDataClass")
@Singleton
class EphemeralPopups @Inject constructor(private val context: Application) {

fun showError(@StringRes messageId: Int) = showLong(messageId)
enum class PopupDuration {
SHORT,
LONG,
INDEFINITE,
}

fun showError(message: String) = showLong(message)
/** Defines functions to render a popup that displays an error message to the user. */
inner class ErrorPopup {
fun show(@StringRes messageId: Int, duration: PopupDuration = PopupDuration.LONG) =
showToast(messageId, duration)

// TODO: Rename to unknownError?
fun showError() = showLong(R.string.unexpected_error)
fun show(message: String, duration: PopupDuration = PopupDuration.LONG) =
showToast(message, duration)

private fun showLong(@StringRes messageId: Int) =
Toast.makeText(context, messageId, Toast.LENGTH_LONG).show()
fun unknownError() = showToast(R.string.unexpected_error, duration = PopupDuration.LONG)

private fun showLong(message: String) = Toast.makeText(context, message, Toast.LENGTH_LONG).show()
private fun showToast(@StringRes messageId: Int, duration: PopupDuration) =
showToast(context.getString(messageId), duration)

private fun showToast(message: String, duration: PopupDuration) {
val dur =
if (duration == PopupDuration.SHORT) {
Toast.LENGTH_SHORT
} else {
// INDEFINITE Length is not supported for toasts; we just use LONG instead for now.
Toast.LENGTH_LONG
}
Toast.makeText(context, message, dur).show()
}
}

/** Defines functions to render a popup that displays an informational message to the user. */
inner class InfoPopup {
fun show(
view: View,
@StringRes messageId: Int,
duration: PopupDuration = PopupDuration.INDEFINITE,
) {
val msg = context.resources.getString(messageId)
showSnackbar(view, msg, duration)
}

private fun showSnackbar(view: View, msg: String, duration: PopupDuration) {
val dur =
when (duration) {
PopupDuration.SHORT -> Snackbar.LENGTH_SHORT
PopupDuration.LONG -> Snackbar.LENGTH_LONG
PopupDuration.INDEFINITE -> Snackbar.LENGTH_INDEFINITE
}
Snackbar.make(view, msg, dur).show()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ internal constructor(

val validationError = taskViewModel.validate()
if (validationError != null) {
popups.get().showError(validationError)
popups.get().ErrorPopup().show(validationError)
return
}
step(-1)
Expand All @@ -164,7 +164,7 @@ internal constructor(
suspend fun onNextClicked(taskViewModel: AbstractTaskViewModel) {
val validationError = taskViewModel.validate()
if (validationError != null) {
popups.get().showError(validationError)
popups.get().ErrorPopup().show(validationError)
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class PhotoTaskFragment : AbstractTaskFragment<PhotoTaskViewModel>() {
capturePhotoLauncher.launch(uri)
Timber.d("Capture photo intent sent")
} catch (e: IllegalArgumentException) {
popups.showError(R.string.error_message)
popups.ErrorPopup().show(R.string.error_message)
Timber.e(e)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class HomeScreenMapContainerFragment : AbstractMapContainerFragment() {
} else {
// Skip data collection screen if the user can't submit any data
// TODO(#1667): Revisit UX for displaying view only mode
ephemeralPopups.showError(getString(R.string.collect_data_viewer_error))
ephemeralPopups.ErrorPopup().show(getString(R.string.collect_data_viewer_error))
}
}
}
Expand Down Expand Up @@ -115,7 +115,7 @@ class HomeScreenMapContainerFragment : AbstractMapContainerFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
savedInstanceState: Bundle?,
): View {
binding = BasemapLayoutBinding.inflate(inflater, container, false)
binding.fragment = this
Expand All @@ -128,6 +128,33 @@ class HomeScreenMapContainerFragment : AbstractMapContainerFragment() {
super.onViewCreated(view, savedInstanceState)
setupMenuFab()
setupBottomLoiCards()
lifecycleScope.launch { showDataCollectionHint() }
}

/**
* Displays a popup hint informing users how to begin collecting data, based on the properties of
* the active survey.
*
* This method should only be called after view creation.
*/
private suspend fun showDataCollectionHint() {
check(this::mapContainerViewModel.isInitialized) {
"showDataCollectionHint called before mapContainerViewModel was initialized"
}
check(this::binding.isInitialized) {
"showDataCollectionHint called before binding was initialized"
}
mapContainerViewModel.surveyUpdateFlow.collect {
val messageId =
when {
it.addLoiPermitted -> R.string.suggest_data_collection_hint
it.readOnly -> R.string.read_only_data_collection_hint
else -> R.string.predefined_data_collection_hint
}
ephemeralPopups
.InfoPopup()
.show(binding.root, messageId, EphemeralPopups.PopupDuration.INDEFINITE)
}
}

private fun setupMenuFab() {
Expand Down Expand Up @@ -183,14 +210,14 @@ class HomeScreenMapContainerFragment : AbstractMapContainerFragment() {
navigator.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToDataCollectionFragment(
cardUiData.loi.id,
cardUiData.loi.job.id
cardUiData.loi.job.id,
)
)
is MapCardUiData.AddLoiCardUiData ->
navigator.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToDataCollectionFragment(
null,
cardUiData.job.id
cardUiData.job.id,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
Expand Down Expand Up @@ -78,6 +80,21 @@ internal constructor(

private val selectedLoiIdFlow = MutableStateFlow<String?>(null)

val activeSurvey: StateFlow<Survey?> = surveyRepository.activeSurveyFlow

/** Captures essential, high-level derived properties for a given survey. */
data class SurveyProperties(val addLoiPermitted: Boolean, val readOnly: Boolean)

/**
* This flow emits [SurveyProperties] when the active survey changes. Callers can use this data to
* determine if and how behavior should change based on differing survey properties.
*/
val surveyUpdateFlow: Flow<SurveyProperties> =
activeSurvey.filterNotNull().map {
val lois = loiRepository.getLocationsOfInterests(it).first()
SurveyProperties(it.jobs.any { it.canDataCollectorsAddLois }, lois.isEmpty())
}

/** Set of [Feature] to render on the map. */
val mapLoiFeatures: Flow<Set<Feature>>

Expand Down Expand Up @@ -105,8 +122,6 @@ internal constructor(
// TODO: Since we depend on survey stream from repo anyway, this transformation can be moved
// into the repository.

val activeSurvey = surveyRepository.activeSurveyFlow

mapLoiFeatures =
activeSurvey.flatMapLatest {
if (it == null) flowOf(setOf())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class StartupFragment : AbstractFragment() {
private fun onInitFailed(t: Throwable) {
Timber.e(t, "Failed to launch app")
if (t is GoogleApiManager.GooglePlayServicesMissingException) {
popups.showError(R.string.google_api_install_failed)
popups.ErrorPopup().show(R.string.google_api_install_failed)
}
requireActivity().finish()
}
Expand Down
3 changes: 3 additions & 0 deletions ground/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,7 @@
<string name="map_location">Map location</string>
<string name="sign_out_dialog_title">Unsynced data</string>
<string name="sign_out_dialog_body">If you sign out, any unsynced data will be discarded</string>
<string name="suggest_data_collection_hint">Zoom in to start collecting data</string>
<string name="read_only_data_collection_hint">Survey is read-only</string>
<string name="predefined_data_collection_hint">Zoom in to a data collection site to collect data</string>
</resources>

0 comments on commit 18f8b41

Please sign in to comment.