Skip to content

Commit

Permalink
Merge branch 'master' into anandwana001/2815/use-active-survey-flow
Browse files Browse the repository at this point in the history
  • Loading branch information
anandwana001 authored Dec 5, 2024
2 parents 222adf4 + 859283c commit 8bac153
Showing 33 changed files with 393 additions and 398 deletions.
Original file line number Diff line number Diff line change
@@ -27,4 +27,6 @@ interface DraftSubmissionDao : BaseDao<DraftSubmissionEntity> {
suspend fun findById(draftSubmissionId: String): DraftSubmissionEntity?

@Query("DELETE FROM draft_submission") fun delete()

@Query("SELECT COUNT(*) FROM draft_submission") suspend fun countAll(): Int
}
Original file line number Diff line number Diff line change
@@ -44,7 +44,6 @@ import com.google.android.ground.persistence.local.room.fields.MutationEntityTyp
import com.google.android.ground.persistence.local.room.fields.UserDetails
import com.google.android.ground.persistence.local.stores.LocalSubmissionStore
import com.google.android.ground.util.Debug.logOnFailure
import com.google.firebase.crashlytics.FirebaseCrashlytics
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.collections.immutable.toPersistentList
@@ -211,9 +210,10 @@ class RoomSubmissionStore @Inject internal constructor() : LocalSubmissionStore
apply(mutation)
enqueue(mutation)
} catch (e: LocalDataStoreException) {
FirebaseCrashlytics.getInstance()
.log("Error enqueueing ${mutation.type} mutation for submission ${mutation.submissionId}")
FirebaseCrashlytics.getInstance().recordException(e)
Timber.e(
e,
"Error enqueueing ${mutation.type} mutation for submission ${mutation.submissionId}",
)
throw e
}
}
@@ -269,4 +269,6 @@ class RoomSubmissionStore @Inject internal constructor() : LocalSubmissionStore
override suspend fun deleteDraftSubmissions() {
draftSubmissionDao.delete()
}

override suspend fun countDraftSubmissions(): Int = draftSubmissionDao.countAll()
}
Original file line number Diff line number Diff line change
@@ -78,4 +78,7 @@ interface LocalSubmissionStore : LocalMutationStore<SubmissionMutation, Submissi

/** Removes all locally stored draft submissions. */
suspend fun deleteDraftSubmissions()

/** Counts the number of draft submissions in the local database. */
suspend fun countDraftSubmissions(): Int
}
Original file line number Diff line number Diff line change
@@ -77,6 +77,10 @@ constructor(
suspend fun getDraftSubmission(draftSubmissionId: String, survey: Survey): DraftSubmission? =
localSubmissionStore.getDraftSubmission(draftSubmissionId = draftSubmissionId, survey = survey)

suspend fun countDraftSubmissions() = localSubmissionStore.countDraftSubmissions()

fun getDraftSubmissionsId() = localValueStore.draftSubmissionId ?: ""

suspend fun saveDraftSubmission(
jobId: String,
loiId: String?,
@@ -103,7 +107,7 @@ constructor(
suspend fun getTotalSubmissionCount(loi: LocationOfInterest) =
loi.submissionCount + getPendingCreateCount(loi.id) - getPendingDeleteCount(loi.id)

private suspend fun getPendingCreateCount(loiId: String) =
suspend fun getPendingCreateCount(loiId: String) =
localSubmissionStore.getPendingCreateCount(loiId)

private suspend fun getPendingDeleteCount(loiId: String) =
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ package com.google.android.ground.ui.common
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.navigation.fragment.findNavController
import com.google.android.ground.R
import com.google.android.ground.coroutines.DefaultDispatcher
import com.google.android.ground.model.geometry.Coordinates
@@ -39,7 +40,6 @@ import timber.log.Timber
abstract class AbstractMapContainerFragment : AbstractFragment() {

@Inject lateinit var map: MapFragment
@Inject lateinit var navigator: Navigator
@Inject lateinit var geocodingManager: GeocodingManager
@Inject @DefaultDispatcher lateinit var defaultDispatcher: CoroutineDispatcher

@@ -101,7 +101,7 @@ abstract class AbstractMapContainerFragment : AbstractFragment() {
/** Opens a dialog for selecting a [MapType] for the basemap layer. */
fun showMapTypeSelectorDialog() {
val types = map.supportedMapTypes.toTypedArray()
navigator.navigate(MapTypeDialogFragmentDirections.showMapTypeDialogFragment(types))
findNavController().navigate(MapTypeDialogFragmentDirections.showMapTypeDialogFragment(types))
}

private fun onLocationLockStateChange(result: Result<Boolean>, map: MapFragment) {
Original file line number Diff line number Diff line change
@@ -30,13 +30,13 @@ import androidx.core.view.doOnLayout
import androidx.hilt.navigation.fragment.hiltNavGraphViewModels
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.viewpager2.widget.ViewPager2
import com.google.android.ground.R
import com.google.android.ground.databinding.DataCollectionFragBinding
import com.google.android.ground.model.task.Task
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.common.BackPressListener
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.home.HomeScreenFragmentDirections
import com.google.android.ground.ui.theme.AppTheme
import dagger.hilt.android.AndroidEntryPoint
@@ -49,7 +49,6 @@ import kotlinx.coroutines.launch
/** Fragment allowing the user to collect data to complete a task. */
@AndroidEntryPoint
class DataCollectionFragment : AbstractFragment(), BackPressListener {
@Inject lateinit var navigator: Navigator
@Inject lateinit var viewPagerAdapterFactory: DataCollectionViewPagerAdapterFactory

val viewModel: DataCollectionViewModel by hiltNavGraphViewModels(R.id.data_collection)
@@ -75,7 +74,7 @@ class DataCollectionFragment : AbstractFragment(), BackPressListener {
binding.dataCollectionToolbar.setNavigationOnClickListener {
isNavigatingUp = true
viewModel.clearDraft()
navigator.navigateUp()
findNavController().navigateUp()
}

return binding.root
@@ -171,7 +170,7 @@ class DataCollectionFragment : AbstractFragment(), BackPressListener {
AppTheme {
DataSubmissionConfirmationDialog {
openAlertDialog.value = false
navigator.navigate(HomeScreenFragmentDirections.showHomeScreen())
findNavController().navigate(HomeScreenFragmentDirections.showHomeScreen())
}
}
}
Original file line number Diff line number Diff line change
@@ -43,7 +43,6 @@ import com.google.android.ground.repository.UserMediaRepository
import com.google.android.ground.system.PermissionDeniedException
import com.google.android.ground.system.PermissionsManager
import com.google.android.ground.ui.common.EphemeralPopups
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.datacollection.components.TaskView
import com.google.android.ground.ui.datacollection.components.TaskViewFactory
import com.google.android.ground.ui.datacollection.tasks.AbstractTaskFragment
@@ -62,7 +61,6 @@ class PhotoTaskFragment : AbstractTaskFragment<PhotoTaskViewModel>() {
@Inject @MainScope lateinit var mainScope: CoroutineScope
@Inject lateinit var permissionsManager: PermissionsManager
@Inject lateinit var popups: EphemeralPopups
@Inject lateinit var navigator: Navigator

// Registers a callback to execute after a user captures a photo from the on-device camera.
private var capturePhotoLauncher: ActivityResultLauncher<Uri> =
Original file line number Diff line number Diff line change
@@ -19,12 +19,12 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.lifecycleScope
import com.google.android.ground.R
import com.google.android.ground.databinding.FragmentDrawAreaTaskBinding
import com.google.android.ground.model.geometry.LineString
import com.google.android.ground.model.geometry.LineString.Companion.lineStringOf
import com.google.android.ground.ui.common.AbstractMapFragmentWithControls.Companion.TASK_ID_FRAGMENT_ARG_KEY
@@ -56,18 +56,27 @@ class DrawAreaTaskFragment @Inject constructor() : AbstractTaskFragment<DrawArea
TaskViewFactory.createWithCombinedHeader(inflater, R.drawable.outline_draw)

override fun onCreateTaskBody(inflater: LayoutInflater): View {
// NOTE(#2493): Multiplying by a random prime to allow for some mathematical "uniqueness".
// Otherwise, the sequentially generated ID might conflict with an ID produced by Google Maps.
val rowLayout = LinearLayout(requireContext()).apply { id = View.generateViewId() * 11411 }
// XML layout is used to provide a static view ID which does not collide with Google Maps view
// ID (https://github.com/google/ground-android/issues/2493).
// The ID is needed when restoring the view on config change since the view is dynamically
// created.
// TODO(https://github.com/google/ground-android/issues/1795):
// Remove this workaround once this UI is migrated to Compose.
val rootView = FragmentDrawAreaTaskBinding.inflate(inflater)

drawAreaTaskMapFragment = drawAreaTaskMapFragmentProvider.get()
val args = Bundle()
args.putString(TASK_ID_FRAGMENT_ARG_KEY, taskId)
drawAreaTaskMapFragment.arguments = args
parentFragmentManager
.beginTransaction()
.add(rowLayout.id, drawAreaTaskMapFragment, DrawAreaTaskMapFragment::class.java.simpleName)
.add(
R.id.container_draw_area_task_map,
drawAreaTaskMapFragment,
DrawAreaTaskMapFragment::class.java.simpleName,
)
.commit()
return rowLayout
return rootView.root
}

override fun onCreateActionButtons() {
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import androidx.core.view.GravityCompat
import androidx.core.view.WindowInsetsCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.ground.BuildConfig
import com.google.android.ground.MainViewModel
import com.google.android.ground.R
@@ -38,7 +39,6 @@ import com.google.android.ground.repository.UserRepository
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.common.BackPressListener
import com.google.android.ground.ui.common.EphemeralPopups
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.theme.AppTheme
import com.google.android.ground.util.systemInsets
import com.google.android.material.imageview.ShapeableImageView
@@ -60,7 +60,6 @@ class HomeScreenFragment :
// make this more intuitive.

@Inject lateinit var ephemeralPopups: EphemeralPopups
@Inject lateinit var navigator: Navigator
@Inject lateinit var userRepository: UserRepository
private lateinit var binding: HomeScreenFragBinding
private lateinit var homeScreenViewModel: HomeScreenViewModel
@@ -95,7 +94,10 @@ class HomeScreenFragment :
binding.navView.setNavigationItemSelectedListener(this)
val navHeader = binding.navView.getHeaderView(0)
navHeader.findViewById<TextView>(R.id.switch_survey_button).setOnClickListener {
homeScreenViewModel.showSurveySelector()
findNavController()
.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToSurveySelectorFragment(false)
)
}
viewLifecycleOwner.lifecycleScope.launch { user = userRepository.getAuthenticatedUser() }
navHeader.findViewById<ShapeableImageView>(R.id.user_image).setOnClickListener {
@@ -105,15 +107,16 @@ class HomeScreenFragment :
// Re-open data collection screen if any drafts are present
viewLifecycleOwner.lifecycleScope.launch {
homeScreenViewModel.getDraftSubmission()?.let { draft ->
navigator.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToDataCollectionFragment(
draft.loiId,
draft.loiName ?: "",
draft.jobId,
true,
SubmissionDeltasConverter.toString(draft.deltas),
findNavController()
.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToDataCollectionFragment(
draft.loiId,
draft.loiName ?: "",
draft.jobId,
true,
SubmissionDeltasConverter.toString(draft.deltas),
)
)
)

ephemeralPopups
.InfoPopup(binding.root, R.string.draft_restored, EphemeralPopups.PopupDuration.SHORT)
@@ -160,11 +163,26 @@ class HomeScreenFragment :

override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.sync_status -> homeScreenViewModel.showSyncStatus()
R.id.nav_offline_areas -> homeScreenViewModel.showOfflineAreas()
R.id.nav_settings -> homeScreenViewModel.showSettings()
R.id.about -> homeScreenViewModel.showAbout()
R.id.terms_of_service -> homeScreenViewModel.showTermsOfService()
R.id.sync_status -> {
findNavController().navigate(HomeScreenFragmentDirections.showSyncStatus())
}
R.id.nav_offline_areas -> {
lifecycleScope.launch {
if (homeScreenViewModel.getOfflineAreas().isEmpty())
findNavController().navigate(HomeScreenFragmentDirections.showOfflineAreaSelector())
else findNavController().navigate(HomeScreenFragmentDirections.showOfflineAreas())
}
}
R.id.nav_settings -> {
findNavController()
.navigate(HomeScreenFragmentDirections.actionHomeScreenFragmentToSettingsActivity())
}
R.id.about -> {
findNavController().navigate(HomeScreenFragmentDirections.showAbout())
}
R.id.terms_of_service -> {
findNavController().navigate(HomeScreenFragmentDirections.showTermsOfService(true))
}
}
closeDrawer()
return true
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.google.android.ground.model.submission.DraftSubmission
import com.google.android.ground.persistence.local.LocalValueStore
import com.google.android.ground.persistence.sync.MediaUploadWorkManager
import com.google.android.ground.persistence.sync.MutationSyncWorkManager
import com.google.android.ground.repository.MutationRepository
@@ -28,7 +27,6 @@ import com.google.android.ground.repository.SubmissionRepository
import com.google.android.ground.repository.SurveyRepository
import com.google.android.ground.repository.UserRepository
import com.google.android.ground.ui.common.AbstractViewModel
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.common.SharedViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -42,8 +40,6 @@ import timber.log.Timber
class HomeScreenViewModel
@Inject
internal constructor(
private val localValueStore: LocalValueStore,
private val navigator: Navigator,
private val offlineAreaRepository: OfflineAreaRepository,
private val submissionRepository: SubmissionRepository,
private val mutationRepository: MutationRepository,
@@ -80,10 +76,10 @@ internal constructor(

/** Attempts to return draft submission for the currently active survey. */
suspend fun getDraftSubmission(): DraftSubmission? {
val draftId = localValueStore.draftSubmissionId
val draftId = submissionRepository.getDraftSubmissionsId()
val survey = surveyRepository.activeSurvey

if (draftId.isNullOrEmpty() || survey == null) {
if (draftId.isEmpty() || survey == null) {
// Missing draft submission
return null
}
@@ -103,38 +99,7 @@ internal constructor(
viewModelScope.launch { _openDrawerRequests.emit(Unit) }
}

fun showSurveySelector() {
navigator.navigate(
HomeScreenFragmentDirections.actionHomeScreenFragmentToSurveySelectorFragment(false)
)
}

private suspend fun getOfflineAreas() = offlineAreaRepository.offlineAreas().first()

fun showOfflineAreas() {
viewModelScope.launch {
navigator.navigate(
if (getOfflineAreas().isEmpty()) HomeScreenFragmentDirections.showOfflineAreaSelector()
else HomeScreenFragmentDirections.showOfflineAreas()
)
}
}

fun showSettings() {
navigator.navigate(HomeScreenFragmentDirections.actionHomeScreenFragmentToSettingsActivity())
}

fun showSyncStatus() {
navigator.navigate(HomeScreenFragmentDirections.showSyncStatus())
}

fun showAbout() {
navigator.navigate(HomeScreenFragmentDirections.showAbout())
}

fun showTermsOfService() {
navigator.navigate(HomeScreenFragmentDirections.showTermsOfService(true))
}
suspend fun getOfflineAreas() = offlineAreaRepository.offlineAreas().first()

fun signOut() {
viewModelScope.launch { userRepository.signOut() }
Loading

0 comments on commit 8bac153

Please sign in to comment.