Skip to content

Alan/generategeodatabasereplicafromfeatureservice migration #297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
e690b96
generate-geodatabase-replica-from-feature-service: initial migration …
alan-edi Dec 18, 2024
9ba05f7
generate-geodatabase-replica-from-feature-service: improve error repo…
alan-edi Dec 23, 2024
0b08389
generate-geodatabase-replica-from-feature-service: delete TODOs
alan-edi Dec 23, 2024
ebd27dc
generate-geodatabase-replica-from-feature-service: rename calculateDo…
alan-edi Dec 23, 2024
237df47
generate-geodatabase-replica-from-feature-service: delete redundant l…
alan-edi Dec 24, 2024
b3cd64a
generate-geodatabase-replica-from-feature-service: unregister the geo…
alan-edi Dec 24, 2024
6aff845
generate-geodatabase-replica-from-feature-service: update README & me…
alan-edi Dec 24, 2024
a5111cc
generate-geodatabase-replica-from-feature-service: update screenshot
alan-edi Dec 24, 2024
a3747c3
Merge branch 'v.next' into alan/generategeodatabasereplicafromfeature…
alan-edi Dec 24, 2024
f430f2d
generate-geodatabase-replica-from-feature-service: code review changes
alan-edi Dec 30, 2024
f2f9bcb
generate-geodatabase-replica-from-feature-service: update screenshot
alan-edi Dec 31, 2024
619ed28
generate-geodatabase-replica-from-feature-service: put onSuccess befo…
alan-edi Jan 7, 2025
0c7ec96
generate-geodatabase-replica-from-feature-service: fix some comments …
alan-edi Jan 7, 2025
bfa10e8
generate-geodatabase-replica-from-feature-service: close geodatabase …
alan-edi Jan 8, 2025
7f87a0e
generate-geodatabase-replica-from-feature-service: replace 4 state va…
alan-edi Jan 13, 2025
53d4eda
generate-geodatabase-replica-from-feature-service: rename UiStatus to…
alan-edi Jan 14, 2025
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 @@ -17,10 +17,6 @@
package com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.components

import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.IntSize
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
Expand All @@ -41,6 +37,8 @@ import com.arcgismaps.tasks.geodatabase.GeodatabaseSyncTask
import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialogViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.File

Expand Down Expand Up @@ -95,19 +93,9 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
// a message dialog view model for handling error messages
val messageDialogVM = MessageDialogViewModel()

// job progress dialog visibility state
var showJobProgressDialog by mutableStateOf(false)
private set

// job progress percentage
var jobProgress by mutableIntStateOf(0)
private set

// state variables indicating if the buttons are enabled
var resetButtonEnabled by mutableStateOf(false)
private set
var generateButtonEnabled by mutableStateOf(false)
private set
// state flow to expose current UI state to UI
private val _uiStateFlow = MutableStateFlow(UiState(status = UiStatus.STARTING))
val uiStateFlow = _uiStateFlow.asStateFlow()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a lot of things called uiState/status here - wonder if some of them could be renamed for clarity.

Not 100% sure what to do: maybe UiStateGenerateGeodatabaseJobStatus or UiStatusGeodatabaseGenerationStatus?

Think the comment could be rewritten too - surely we're exposing application state to the UI, not UI state? UI state is the product of whatever data we pass to the UI.


// create a GeodatabaseSyncTask with the URL of the feature service
private var geodatabaseSyncTask = GeodatabaseSyncTask(FEATURE_SERVICE_URL)
Expand All @@ -124,21 +112,21 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(

viewModelScope.launch {
// load the map
arcGISMap.load().onFailure { error ->
messageDialogVM.showMessageDialog(
title = "Failed to load map",
description = error.message.toString()
)
}.onSuccess {
arcGISMap.load().onSuccess {
// load the GeodatabaseSyncTask
geodatabaseSyncTask.load().onFailure { error ->
geodatabaseSyncTask.load().onSuccess {
_uiStateFlow.value = UiState(status = UiStatus.READY_FOR_GENERATE)
}.onFailure { error ->
messageDialogVM.showMessageDialog(
title = "Failed to load GeodatabaseSyncTask",
description = error.message.toString()
)
}.onSuccess {
generateButtonEnabled = true
}
}.onFailure { error ->
messageDialogVM.showMessageDialog(
title = "Failed to load map",
description = error.message.toString()
)
}
}
}
Expand Down Expand Up @@ -178,7 +166,7 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
* Reset the map to its original state.
*/
fun resetMap() {
// clear any layers and symbols already on the map
// clear any layers and symbols already on the map
arcGISMap.operationalLayers.clear()
graphicsOverlay.graphics.clear()
// add the download area boundary
Expand All @@ -187,17 +175,14 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
arcGISMap.operationalLayers.add(featureLayer)
// close the current geodatabase, if a replica was already generated
geodatabase?.close()
// show the Generate button
generateButtonEnabled = true
resetButtonEnabled = false
_uiStateFlow.value = UiState(status = UiStatus.READY_FOR_GENERATE)
}

/**
* Generate the geodatabase replica.
*/
fun generateGeodatabaseReplica() {
// disable the Generate button
generateButtonEnabled = false
_uiStateFlow.value = UiState(status = UiStatus.GENERATING, jobProgress = 0)

val offlineGeodatabasePath =
application.getExternalFilesDir(null)?.path + "/portland_trees_gdb.geodatabase"
Expand Down Expand Up @@ -250,18 +235,14 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
// create a flow-collection for the job's progress
viewModelScope.launch(Dispatchers.Main) {
job.progress.collect { progress ->
jobProgress = progress
_uiStateFlow.value = UiState(status = UiStatus.GENERATING, jobProgress = progress)
}
}

// show the Job Progress Dialog
showJobProgressDialog = true

// start the job and wait for Job result
job.start()
job.result().onSuccess { geodatabase ->
// dismiss the progress dialog and display the data
showJobProgressDialog = false
// display the data
loadGeodatabaseAndAddToMap(geodatabase)

// unregister the geodatabase since we will not sync changes to the service
Expand All @@ -272,12 +253,11 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
)
}
}.onFailure { error ->
_uiStateFlow.value = UiState(status = UiStatus.READY_FOR_GENERATE)
messageDialogVM.showMessageDialog(
title = "Error generating geodatabase",
description = error.message.toString()
)
showJobProgressDialog = false
generateButtonEnabled = true
}
}

Expand All @@ -297,13 +277,14 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
}
// keep track of the geodatabase to close it before generating a new replica
geodatabase = replicaGeodatabase
_uiStateFlow.value = UiState(status = UiStatus.REPLICA_DISPLAYED)
}.onFailure { error ->
_uiStateFlow.value = UiState(status = UiStatus.READY_FOR_GENERATE)
messageDialogVM.showMessageDialog(
title = "Error loading geodatabase",
description = error.message.toString()
)
}
resetButtonEnabled = true
}

/**
Expand All @@ -313,6 +294,27 @@ class GenerateGeodatabaseReplicaFromFeatureServiceViewModel(
viewModelScope.launch(Dispatchers.IO) {
generateGeodatabaseJob?.cancel()
}
generateButtonEnabled = true
_uiStateFlow.value = UiState(status = UiStatus.READY_FOR_GENERATE)
}

override fun onCleared() {
super.onCleared()
// close the current geodatabase, if any, to release internal resources and file locks
geodatabase?.close()
}
}

/**
* Data class representing the UI state.
*/
data class UiState(
val status: UiStatus,
val jobProgress: Int = 0
)

enum class UiStatus {
STARTING,
READY_FOR_GENERATE,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reads slightly odd to me, how about READY_TO_GENERATE or READY_FOR_GENERATION?

GENERATING,
REPLICA_DISPLAYED
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add newline

Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.getString
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.arcgismaps.LoadStatus
import com.arcgismaps.toolkit.geoviewcompose.MapView
import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.components.GenerateGeodatabaseReplicaFromFeatureServiceViewModel
import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.R
import com.esri.arcgismaps.sample.generategeodatabasereplicafromfeatureservice.components.UiStatus
import com.esri.arcgismaps.sample.sampleslib.components.JobLoadingDialog
import com.esri.arcgismaps.sample.sampleslib.components.MessageDialog
import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
Expand All @@ -47,6 +50,7 @@ import com.esri.arcgismaps.sample.sampleslib.components.SampleTopAppBar
fun GenerateGeodatabaseReplicaFromFeatureServiceScreen(sampleName: String) {
val application = LocalContext.current.applicationContext
val mapViewModel: GenerateGeodatabaseReplicaFromFeatureServiceViewModel = viewModel()
val uiState by mapViewModel.uiStateFlow.collectAsStateWithLifecycle()
Scaffold(
topBar = { SampleTopAppBar(title = sampleName) },
content = {
Expand Down Expand Up @@ -86,32 +90,30 @@ fun GenerateGeodatabaseReplicaFromFeatureServiceScreen(sampleName: String) {
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceEvenly
) {
// the Reset Map button
Button(
onClick = {
mapViewModel.resetMap()
},
enabled = mapViewModel.resetButtonEnabled
enabled = uiState.status == UiStatus.REPLICA_DISPLAYED
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On naming: uiState.status feels particularly redundant/confusing.

) {
Text(text = getString(application, R.string.reset_map))
}

// the Generate button
Button(
onClick = {
mapViewModel.generateGeodatabaseReplica()
},
enabled = mapViewModel.generateButtonEnabled
enabled = uiState.status == UiStatus.READY_FOR_GENERATE
) {
Text(text = getString(application, R.string.generate_button_text))
}
}

// display progress dialog while generating a geodatabase replica
if (mapViewModel.showJobProgressDialog) {
if (uiState.status == UiStatus.GENERATING) {
JobLoadingDialog(
title = "Generating geodatabase replica...",
progress = mapViewModel.jobProgress,
title = getString(application, R.string.dialog_title),
progress = uiState.jobProgress,
cancelJobRequest = { mapViewModel.cancelOfflineGeodatabaseJob() }
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<resources>
<string name="generate_geodatabase_replica_from_feature_service_app_name">Generate geodatabase replica from feature service</string>
<string name="generate_button_text">Generate</string>
<string name="dialog_title">Fetching result</string>
<string name="dialog_title">Generating geodatabase replica...</string>
<string name="reset_map">Reset map</string>
</resources>
Loading