Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ internal constructor(
val uiState: StateFlow<DataCollectionUiState> = _uiState

val loiNameDialogOpen = mutableStateOf(false)

private val _loiNameDraft = MutableStateFlow("")
val loiNameDraft: StateFlow<String> = _loiNameDraft

private var shouldLoadFromDraft: Boolean = savedStateHandle[TASK_SHOULD_LOAD_FROM_DRAFT] ?: false

private val jobId: String = requireNotNull(savedStateHandle[TASK_JOB_ID_KEY])
Expand Down Expand Up @@ -126,6 +130,30 @@ internal constructor(
}
}

fun setLoiNameDraft(name: String) {
_loiNameDraft.value = name
}

fun getLoiName(): String {
val state = uiState.value
return (state as? DataCollectionUiState.Ready)?.loiName ?: getTypedLoiNameOrEmpty()
}

fun openLoiNameDialog() {
setLoiNameDraft(getLoiName())
loiNameDialogOpen.value = true
}

fun confirmLoiName(name: String) {
loiNameDialogOpen.value = false
setLoiName(name)
}

fun dismissLoiNameDialog(initialName: String) {
loiNameDialogOpen.value = false
setLoiNameDraft(initialName)
}

private fun isFirstPosition(taskId: String): Boolean =
withReadyOrNull { taskSequenceHandler.isFirstPosition(taskId) } ?: false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ abstract class AbstractTaskFragment<T : AbstractTaskViewModel> : AbstractFragmen

private fun handleNext() {
if (getTask().isAddLoiTask) {
dataCollectionViewModel.loiNameDialogOpen.value = true
dataCollectionViewModel.openLoiNameDialog()
} else {
moveToNext()
}
Expand Down Expand Up @@ -187,11 +187,23 @@ abstract class AbstractTaskFragment<T : AbstractTaskViewModel> : AbstractFragmen

/** Handles actions triggered from the task screen UI. */
fun handleTaskScreenAction(screenAction: TaskScreenAction) {
if (screenAction is TaskScreenAction.OnButtonClicked) {
handleButtonClick(screenAction.action)
} else {
// TODO: Handle other actions
// https://github.com/google/ground-android/issues/3630
when (screenAction) {
is TaskScreenAction.OnButtonClicked -> {
handleButtonClick(screenAction.action)
}
is TaskScreenAction.OnLoiNameChanged -> {
dataCollectionViewModel.setLoiNameDraft(screenAction.name)
}
is TaskScreenAction.OnLoiNameDismiss -> {
dataCollectionViewModel.dismissLoiNameDialog(dataCollectionViewModel.getLoiName())
}
is TaskScreenAction.OnLoiNameConfirm -> {
dataCollectionViewModel.confirmLoiName(screenAction.name)
moveToNext()
}
else -> {
// Remaining actions are task specific and are handled within the task screen.
}
}
}

Expand All @@ -215,7 +227,7 @@ abstract class AbstractTaskFragment<T : AbstractTaskViewModel> : AbstractFragmen
private fun getTask(): Task = viewModel.task

@Composable
private fun LoiNameDialog() {
protected fun LoiNameDialog() {
var openAlertDialog by dataCollectionViewModel.loiNameDialogOpen

if (openAlertDialog) {
Expand All @@ -242,13 +254,13 @@ abstract class AbstractTaskFragment<T : AbstractTaskViewModel> : AbstractFragmen

@Composable
private fun InstructionsDialog(instructionData: InstructionData) {
var showInstructionsDialog by viewModel.showInstructionsDialog
val showInstructionsDialog by viewModel.showInstructionsDialog.collectAsStateWithLifecycle()

if (showInstructionsDialog) {
InstructionsDialog(
data = instructionData,
onDismissed = {
showInstructionsDialog = false
viewModel.dismissInstructions()
onInstructionDialogDismissed()
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.fragment.hiltNavGraphViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.asFlow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import java.math.RoundingMode
import java.text.DecimalFormat
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import org.groundplatform.android.R
import org.groundplatform.android.common.Constants.ACCURACY_THRESHOLD_IN_M
Expand Down Expand Up @@ -151,14 +150,14 @@ abstract class AbstractTaskMapFragment<TVM : AbstractTaskViewModel> :
override fun onMapReady(map: MapFragment) {
launchWhenTaskVisible(dataCollectionViewModel, taskId) {
launch { getMapViewModel().getCurrentCameraPosition().collect { onMapCameraMoved(it) } }
launch { renderFeatures().asFlow().collect { map.setFeatures(it) } }
launch { renderFeatures().collect { map.setFeatures(it) } }
// Allow the fragment to restore map viewport to previously drawn feature.
setDefaultViewPort()
}
}

/** Must be overridden by subclasses. */
open fun renderFeatures(): LiveData<Set<Feature>> = MutableLiveData(setOf())
open fun renderFeatures(): Flow<Set<Feature>> = flowOf(setOf())

/**
* This should be overridden if the fragment wants to set a custom map camera position. Default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package org.groundplatform.android.ui.datacollection.tasks

import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
Expand Down Expand Up @@ -43,7 +42,8 @@ abstract class AbstractTaskViewModel internal constructor() : AbstractViewModel(
private val _taskDataFlow: MutableStateFlow<TaskData?> = MutableStateFlow(null)
val taskTaskData: StateFlow<TaskData?> = _taskDataFlow.asStateFlow()

val showInstructionsDialog = mutableStateOf(false)
private val _showInstructionsDialog = MutableStateFlow(false)
val showInstructionsDialog = _showInstructionsDialog.asStateFlow()

open val taskActionButtonStates: StateFlow<List<ButtonActionState>> by lazy {
taskTaskData
Expand All @@ -56,6 +56,14 @@ abstract class AbstractTaskViewModel internal constructor() : AbstractViewModel(
lateinit var task: Task
private lateinit var taskPositionInterface: TaskPositionInterface

fun dismissInstructions() {
_showInstructionsDialog.value = false
}

fun showInstructions() {
_showInstructionsDialog.value = true
}

open fun initialize(
job: Job,
task: Task,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,42 @@
*/
package org.groundplatform.android.ui.datacollection.tasks.point

import androidx.compose.runtime.Composable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import javax.inject.Provider
import org.groundplatform.android.R
import org.groundplatform.android.ui.datacollection.components.InstructionData
import org.groundplatform.android.ui.datacollection.components.TaskHeader
import org.groundplatform.android.ui.datacollection.components.TaskMapFragmentContainer
import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskFragment
import org.groundplatform.android.util.createComposeView

@AndroidEntryPoint
class DropPinTaskFragment @Inject constructor() : AbstractTaskFragment<DropPinTaskViewModel>() {
@Inject lateinit var dropPinTaskMapFragmentProvider: Provider<DropPinTaskMapFragment>

override val taskHeader: TaskHeader by lazy {
TaskHeader(viewModel.task.label, R.drawable.outline_pin_drop)
}

override val instructionData =
InstructionData(iconId = R.drawable.swipe_24, stringId = R.string.drop_a_pin_tooltip_text)

@Composable
override fun TaskBody() {
TaskMapFragmentContainer(
taskId = viewModel.task.id,
fragmentManager = childFragmentManager,
fragmentProvider = dropPinTaskMapFragmentProvider,
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
) = createComposeView {
val shouldShowLoiNameDialog by dataCollectionViewModel.loiNameDialogOpen
val loiName by dataCollectionViewModel.loiNameDraft.collectAsStateWithLifecycle()

override fun onTaskResume() {
if (isVisible && viewModel.shouldShowInstructionsDialog()) {
viewModel.showInstructionsDialog.value = true
DropPinTaskScreen(
viewModel = viewModel,
onFooterPositionUpdated = { saveFooterPosition(it) },
shouldShowLoiNameDialog = shouldShowLoiNameDialog,
loiName = loiName,
onAction = { action -> handleTaskScreenAction(action) },
) {
TaskMapFragmentContainer(
taskId = viewModel.task.id,
fragmentManager = childFragmentManager,
fragmentProvider = dropPinTaskMapFragmentProvider,
)
}
}

override fun onInstructionDialogDismissed() {
viewModel.instructionsDialogShown = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
*/
package org.groundplatform.android.ui.datacollection.tasks.point

import androidx.lifecycle.LiveData
import androidx.lifecycle.asFlow
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import org.groundplatform.android.model.map.CameraPosition
import org.groundplatform.android.ui.datacollection.tasks.AbstractTaskMapFragment
import org.groundplatform.android.ui.datacollection.tasks.launchWhenTaskVisible
Expand All @@ -34,14 +33,12 @@ class DropPinTaskMapFragment @Inject constructor() :

// Disable pan/zoom gestures if a marker has been placed on the map.
launchWhenTaskVisible(dataCollectionViewModel, taskId) {
taskViewModel.features.asFlow().collect { features ->
updateGestures(features, taskViewModel.captureLocation)
}
taskViewModel.features.collect { features -> updateGestures(features) }
}
}

private fun updateGestures(features: Set<Feature>, captureLocation: Boolean) {
if (features.isNotEmpty() || captureLocation) {
private fun updateGestures(features: Set<Feature>) {
if (features.isNotEmpty()) {
map.disableGestures()
} else {
map.enableGestures()
Expand All @@ -53,7 +50,7 @@ class DropPinTaskMapFragment @Inject constructor() :
taskViewModel.updateCameraPosition(position)
}

override fun renderFeatures(): LiveData<Set<Feature>> = taskViewModel.features
override fun renderFeatures(): Flow<Set<Feature>> = taskViewModel.features

override fun setDefaultViewPort() {
val feature = taskViewModel.features.value?.firstOrNull() ?: return
Expand Down
Loading
Loading