Skip to content
Merged
Prev Previous commit
Next Next commit
Implement layouts
  • Loading branch information
grzesiek2010 committed Jan 28, 2026
commit fb86f341f4578ca80c46e9a18b670a74533c1619
6 changes: 6 additions & 0 deletions geo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
alias(libs.plugins.kotlinKapt)
alias(libs.plugins.composeCompiler)
}

apply(from = "../config/quality.gradle")
Expand Down Expand Up @@ -69,6 +70,11 @@ dependencies {
exclude(group = "org.hamcrest", module = "hamcrest-all")
}

implementation(libs.androidXComposeMaterial)
implementation(libs.androidXComposeMaterialIcons)
implementation(libs.androidXComposePreview)
debugImplementation(libs.androidXComposeTooling)

debugImplementation(project(":fragments-test"))

testImplementation(project(":androidtest"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class GeoPolyFragment @JvmOverloads constructor(
fun initMap(newMapFragment: MapFragment?, binding: GeopolyLayoutBinding) {
map = newMapFragment

binding.info.setOnClickListener { InfoDialog.show(requireContext(), InfoDialog.Type.MANUAL_FROM_INFO_BUTTON) }
binding.clear.setOnClickListener { showClearDialog() }
binding.pause.setOnClickListener {
viewModel.stopRecording()
Expand Down
198 changes: 198 additions & 0 deletions geo/src/main/java/org/odk/collect/geo/geopoly/InfoDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package org.odk.collect.geo.geopoly

import android.content.Context
import androidx.appcompat.app.AlertDialog
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Backspace
import androidx.compose.material.icons.automirrored.filled.DirectionsWalk
import androidx.compose.material.icons.filled.AddLocation
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.TouchApp
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.tooling.preview.Preview
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.odk.collect.androidshared.R.dimen
import org.odk.collect.androidshared.ui.ComposeThemeProvider.Companion.setContextThemedContent

object InfoDialog {
data class InfoItem(
val icon: ImageVector,
val text: String
)

enum class Type {
PLACEMENT_FROM_SNACKBAR,
PLACEMENT_FROM_INFO_BUTTON,
MANUAL_FROM_SNACKBAR,
MANUAL_FROM_INFO_BUTTON,
}

fun show(context: Context, type: Type) {
var dialog: AlertDialog? = null

val info = ComposeView(context).apply {
setContextThemedContent {
when (type) {
Type.PLACEMENT_FROM_SNACKBAR -> PlacementFromSnackbarInfo { dialog?.dismiss() }
Type.PLACEMENT_FROM_INFO_BUTTON -> PlacementFromInfoButtonInfo { dialog?.dismiss() }
Type.MANUAL_FROM_SNACKBAR -> ManualFromSnackbarInfo { dialog?.dismiss() }
Type.MANUAL_FROM_INFO_BUTTON -> ManualFromInfoButtonInfo { dialog?.dismiss() }
}
}
}

dialog = MaterialAlertDialogBuilder(context)
.setView(info)
.show()
}
}

@Composable
private fun PlacementFromSnackbarInfo(onDone: () -> Unit) {
InfoContent(
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Long press to move point"),
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.Backspace, "Remove last point"),
InfoDialog.InfoItem(Icons.Filled.Delete, "Delete shape to start over"),
InfoDialog.InfoItem(Icons.Filled.AddLocation, "Add point"),
onDone = onDone
)
}

@Composable
private fun PlacementFromInfoButtonInfo(onDone: () -> Unit) {
InfoContent(
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Tap to add a point"),
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Long press to move point"),
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.Backspace, "Remove last point"),
InfoDialog.InfoItem(Icons.Filled.Delete, "Delete entire shape"),
onDone = onDone
)
}

@Composable
private fun ManualFromSnackbarInfo(onDone: () -> Unit) {
InfoContent(
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.DirectionsWalk, "Physically move to correct"),
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Long press to move point"),
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.Backspace, "Remove last point"),
InfoDialog.InfoItem(Icons.Filled.Delete, "Delete entire shape"),
onDone = onDone
)
}

@Composable
private fun ManualFromInfoButtonInfo(onDone: () -> Unit) {
InfoContent(
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Tap to add a point"),
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.DirectionsWalk, "Physically move to correct"),
InfoDialog.InfoItem(Icons.Filled.TouchApp, "Long press to move point"),
InfoDialog.InfoItem(Icons.AutoMirrored.Filled.Backspace, "Remove last point"),
InfoDialog.InfoItem(Icons.Filled.Delete, "Delete entire shape"),
onDone = onDone
)
}

@Composable
private fun Title() {
Text(
modifier = Modifier.padding(
start = dimensionResource(id = dimen.margin_standard),
top = dimensionResource(id = dimen.margin_extra_small),
bottom = dimensionResource(id = dimen.margin_standard)
),
text = "How to modify the map",
style = MaterialTheme.typography.titleLarge
)
}

@Composable
private fun Info(icon: ImageVector, text: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(id = dimen.margin_standard)),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = null,
)
Text(
modifier = Modifier.padding(start = dimensionResource(id = dimen.margin_small)),
text = text,
style = MaterialTheme.typography.bodyLarge
)
}
}

@Composable
private fun InfoContent(
vararg items: InfoDialog.InfoItem,
onDone: () -> Unit
) {
Column(
modifier = Modifier.padding(dimensionResource(id = dimen.margin_standard))
) {
Title()
items.forEachIndexed { index, item ->
Info(item.icon, item.text)
if (index < items.lastIndex) {
HorizontalDivider(
Modifier.padding(horizontal = dimensionResource(id = dimen.margin_small))
)
}
}
DoneButton(onDone)
}
}

@Composable
private fun DoneButton(onDone: () -> Unit) {
Row(
modifier = Modifier.fillMaxWidth().padding(top = dimensionResource(id = dimen.margin_standard)),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = onDone) {
Text("Done")
}
}
}

@Preview(showBackground = true)
@Composable
private fun PlacementFromSnackbarInfoPreview() {
PlacementFromSnackbarInfo {}
}

@Preview(showBackground = true)
@Composable
private fun PlacementFromInfoButtonInfoPreview() {
PlacementFromInfoButtonInfo {}
}

@Preview(showBackground = true)
@Composable
private fun ManualFromSnackbarInfoPreview() {
ManualFromSnackbarInfo {}
}

@Preview(showBackground = true)
@Composable
private fun ManualFromInfoButtonInfoPreview() {
ManualFromInfoButtonInfo {}
}