Skip to content

Commit

Permalink
Removed Navigator from TermsOfServiceFragment, SurveySelectorFragment…
Browse files Browse the repository at this point in the history
… and OfflineAreasFragment (#2889)

* added navigation check

* added lifecycle state

* coroutineScope fix

* navigate termsOfService to surveySelector

* remove navigator

* tests fixes

* test fix

* removed viewlifecycle scope
  • Loading branch information
anandwana001 authored Dec 5, 2024
1 parent 9ab91bc commit 8349e55
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.ground.databinding.OfflineAreasFragBinding
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.theme.AppTheme
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.launch

/**
* Fragment containing a list of downloaded areas on the device. An area is a set of offline raster
Expand All @@ -42,7 +43,6 @@ import javax.inject.Inject
@AndroidEntryPoint
class OfflineAreasFragment : AbstractFragment() {

@Inject lateinit var navigator: Navigator
private lateinit var viewModel: OfflineAreasViewModel

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -66,14 +66,23 @@ class OfflineAreasFragment : AbstractFragment() {
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewModel.navigateToOfflineAreaSelector.collect {
findNavController().navigate(OfflineAreasFragmentDirections.showOfflineAreaSelector())
}
}
}

@Composable
private fun ShowOfflineAreas() {
val list by viewModel.offlineAreas.observeAsState()
list?.let {
LazyColumn(Modifier.fillMaxSize().testTag("offline area list")) {
items(it) {
OfflineAreaListItem(offlineAreaDetails = it) { areaId ->
navigator.navigate(OfflineAreasFragmentDirections.viewOfflineArea(areaId))
findNavController().navigate(OfflineAreasFragmentDirections.viewOfflineArea(areaId))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,26 @@ package com.google.android.ground.ui.offlineareas

import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.google.android.ground.model.imagery.OfflineArea
import com.google.android.ground.repository.OfflineAreaRepository
import com.google.android.ground.ui.common.AbstractViewModel
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.util.toMb
import com.google.android.ground.util.toMbString
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch

/**
* View model for the offline area manager fragment. Handles the current list of downloaded areas.
*/
class OfflineAreasViewModel
@Inject
internal constructor(
private val navigator: Navigator,
private val offlineAreaRepository: OfflineAreaRepository,
) : AbstractViewModel() {
internal constructor(private val offlineAreaRepository: OfflineAreaRepository) :
AbstractViewModel() {

/**
* Returns the current list of downloaded offline map imagery areas available for viewing. If an
Expand All @@ -48,6 +49,9 @@ internal constructor(
val showNoAreasMessage: LiveData<Boolean>
val showProgressSpinner: LiveData<Boolean>

private val _navigateToOfflineAreaSelector = MutableSharedFlow<Unit>(replay = 0)
val navigateToOfflineAreaSelector = _navigateToOfflineAreaSelector.asSharedFlow()

init {
val offlineAreas =
offlineAreaRepository.offlineAreas().map { list -> list.map { toOfflineAreaDetails(it) } }
Expand All @@ -65,6 +69,6 @@ internal constructor(

/** Navigate to the area selector for offline map imagery. */
fun showOfflineAreaSelector() {
navigator.navigate(OfflineAreasFragmentDirections.showOfflineAreaSelector())
viewModelScope.launch { _navigateToOfflineAreaSelector.emit(Unit) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.ground.databinding.OfflineAreaSelectorFragBinding
import com.google.android.ground.ui.common.AbstractMapContainerFragment
import com.google.android.ground.ui.common.BaseMapViewModel
Expand Down Expand Up @@ -64,6 +65,20 @@ class OfflineAreaSelectorFragment : AbstractMapContainerFragment() {
viewModel.isDownloadProgressVisible.observe(viewLifecycleOwner) {
showDownloadProgressDialog(it)
}

lifecycleScope.launch {
viewModel.navigate.collect {
when (it) {
is UiState.OfflineAreaBackToHomeScreen -> {
findNavController()
.navigate(OfflineAreaSelectorFragmentDirections.offlineAreaBackToHomescreen())
}
is UiState.Up -> {
findNavController().navigateUp()
}
}
}
}
}

override fun onMapReady(map: MapFragment) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import com.google.android.ground.system.PermissionsManager
import com.google.android.ground.system.SettingsManager
import com.google.android.ground.ui.common.BaseMapViewModel
import com.google.android.ground.ui.common.MapConfig
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.common.SharedViewModel
import com.google.android.ground.ui.map.Bounds
import com.google.android.ground.ui.map.CameraPosition
Expand All @@ -38,6 +37,8 @@ import com.google.android.ground.util.toMb
import com.google.android.ground.util.toMbString
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

private const val MIN_DOWNLOAD_ZOOM_LEVEL = 9
Expand All @@ -49,7 +50,6 @@ class OfflineAreaSelectorViewModel
@Inject
internal constructor(
private val offlineAreaRepository: OfflineAreaRepository,
private val navigator: Navigator,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
private val resources: Resources,
locationManager: LocationManager,
Expand Down Expand Up @@ -78,6 +78,9 @@ internal constructor(
val bottomText = MutableLiveData<String?>(null)
val downloadButtonEnabled = MutableLiveData(false)

private val _navigate = MutableSharedFlow<UiState>(replay = 0)
val navigate = _navigate.asSharedFlow()

override val mapConfig: MapConfig
get() =
super.mapConfig.copy(
Expand Down Expand Up @@ -105,12 +108,12 @@ internal constructor(
downloadProgress.postValue(progressValue)
}
isDownloadProgressVisible.postValue(false)
navigator.navigate(OfflineAreaSelectorFragmentDirections.offlineAreaBackToHomescreen())
_navigate.emit(UiState.OfflineAreaBackToHomeScreen)
}
}

fun onCancelClick() {
navigator.navigateUp()
viewModelScope.launch { _navigate.emit(UiState.Up) }
}

override fun onMapDragged() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.android.ground.ui.offlineareas.selector

sealed class UiState {

data object OfflineAreaBackToHomeScreen : UiState()

data object Up : UiState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.ground.databinding.OfflineAreaViewerFragBinding
import com.google.android.ground.ui.common.AbstractMapContainerFragment
import com.google.android.ground.ui.common.BaseMapViewModel
Expand Down Expand Up @@ -67,5 +68,10 @@ class OfflineAreaViewerFragment @Inject constructor() : AbstractMapContainerFrag
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch { viewModel.navigateUp.collect { findNavController().navigateUp() } }
}

override fun getMapViewModel(): BaseMapViewModel = viewModel
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ import com.google.android.ground.system.PermissionsManager
import com.google.android.ground.system.SettingsManager
import com.google.android.ground.ui.common.BaseMapViewModel
import com.google.android.ground.ui.common.MapConfig
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.map.MapType
import com.google.android.ground.util.toMb
import com.google.android.ground.util.toMbString
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import timber.log.Timber

Expand All @@ -45,7 +46,6 @@ class OfflineAreaViewerViewModel
@Inject
constructor(
private val offlineAreaRepository: OfflineAreaRepository,
private val navigator: Navigator,
locationManager: LocationManager,
mapStateRepository: MapStateRepository,
settingsManager: SettingsManager,
Expand All @@ -70,6 +70,9 @@ constructor(
val areaSize = MutableLiveData<String>()
val progressOverlayVisible = MutableLiveData<Boolean>()

private val _navigateUp = MutableSharedFlow<Unit>(replay = 0)
val navigateUp = _navigateUp.asSharedFlow()

override val mapConfig: MapConfig
get() =
super.mapConfig.copy(
Expand All @@ -86,7 +89,7 @@ constructor(
area.postValue(it)
areaSize.postValue(offlineAreaRepository.sizeOnDevice(it).toMb().toMbString())
areaName.postValue(it.name)
} ?: run { navigator.navigateUp() }
} ?: run { _navigateUp.emit(Unit) }
}
}

Expand All @@ -100,6 +103,6 @@ constructor(
if (deletedArea == null) return
Timber.d("Removing offline area ${deletedArea.name}")
offlineAreaRepository.removeFromDevice(deletedArea)
navigator.navigateUp()
_navigateUp.emit(Unit)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.PopupMenu
import androidx.navigation.fragment.findNavController
import com.google.android.ground.R
import com.google.android.ground.databinding.SurveySelectorFragBinding
import com.google.android.ground.model.SurveyListItem
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.home.HomeScreenFragmentDirections
import com.google.android.ground.util.visibleIf
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
Expand Down Expand Up @@ -69,6 +71,9 @@ class SurveySelectorFragment : AbstractFragment(), BackPressListener {
dismissProgressDialog()
ephemeralPopups.ErrorPopup().unknownError()
}
is UiState.NavigateToHome -> {
findNavController().navigate(HomeScreenFragmentDirections.showHomeScreen())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import com.google.android.ground.repository.SurveyRepository
import com.google.android.ground.repository.UserRepository
import com.google.android.ground.system.auth.AuthenticationManager
import com.google.android.ground.ui.common.AbstractViewModel
import com.google.android.ground.ui.common.Navigator
import com.google.android.ground.ui.home.HomeScreenFragmentDirections
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
Expand All @@ -44,7 +42,6 @@ class SurveySelectorViewModel
internal constructor(
private val surveyRepository: SurveyRepository,
private val authManager: AuthenticationManager,
private val navigator: Navigator,
private val activateSurveyUseCase: ActivateSurveyUseCase,
@ApplicationScope private val externalScope: CoroutineScope,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
Expand Down Expand Up @@ -89,7 +86,7 @@ internal constructor(
onSuccess = {
surveyActivationInProgress = false
_uiState.emit(UiState.SurveyActivated)
navigateToHomeScreen()
_uiState.emit(UiState.NavigateToHome)
},
onFailure = { e ->
Timber.e(e, "Failed to activate survey")
Expand All @@ -100,10 +97,6 @@ internal constructor(
}
}

private fun navigateToHomeScreen() {
navigator.navigate(HomeScreenFragmentDirections.showHomeScreen())
}

fun deleteSurvey(surveyId: String) {
externalScope.launch(ioDispatcher) { surveyRepository.removeOfflineSurvey(surveyId) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ sealed class UiState {

/** Represents that there was an error while activating surveys. */
data object Error : UiState()

data object NavigateToHome : UiState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.ground.databinding.FragmentTermsServiceBinding
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.surveyselector.SurveySelectorFragmentDirections
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch

@AndroidEntryPoint
class TermsOfServiceFragment : AbstractFragment() {
Expand All @@ -45,4 +49,14 @@ class TermsOfServiceFragment : AbstractFragment() {
binding.lifecycleOwner = this
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycleScope.launch {
viewModel.navigateToSurveySelector.collect {
findNavController()
.navigate(SurveySelectorFragmentDirections.showSurveySelectorScreen(true))
}
}
}
}
Loading

0 comments on commit 8349e55

Please sign in to comment.