From 371d3a61a2132fc37fcd86750fa8b96c2906cc8b Mon Sep 17 00:00:00 2001 From: Sameri11 <51940183+Sameri11@users.noreply.github.com> Date: Tue, 24 Dec 2024 13:18:17 +0700 Subject: [PATCH] [SDK-#] add compose-map package examples --- app/build.gradle | 16 +++ app/config/androidLint/baseline.xml | 110 ++++++------------ app/src/main/AndroidManifest.xml | 4 + .../java/ru/dgis/sdk/demo/ComposeActivity.kt | 33 ++++++ .../java/ru/dgis/sdk/demo/MainActivity.kt | 4 + .../ru/dgis/sdk/demo/compose/CaptionSlider.kt | 20 ++++ .../ru/dgis/sdk/demo/compose/EnumToggle.kt | 43 +++++++ .../ru/dgis/sdk/demo/compose/MapControls.kt | 58 +++++++++ .../ru/dgis/sdk/demo/compose/MapViewModel.kt | 33 ++++++ .../dgis/sdk/demo/compose/MarkerViewModel.kt | 42 +++++++ .../ru/dgis/sdk/demo/compose/RGBAImage.kt | 21 ++++ .../MapCopyrightGravityConfigurator.kt | 45 +++++++ .../MapCopyrightMarginsConfigurator.kt | 27 +++++ .../configurators/MapFpsConfigurator.kt | 57 +++++++++ .../configurators/MapThemeConfigurator.kt | 37 ++++++ .../configurators/MarkerConfigurator.kt | 82 +++++++++++++ .../demo/compose/screens/ComposeScreenData.kt | 11 ++ .../demo/compose/screens/ControlsScreen.kt | 30 +++++ .../demo/compose/screens/CopyrightScreen.kt | 61 ++++++++++ .../sdk/demo/compose/screens/FpsScreen.kt | 66 +++++++++++ .../sdk/demo/compose/screens/HomeScreen.kt | 92 +++++++++++++++ .../sdk/demo/compose/screens/MarkersScreen.kt | 77 ++++++++++++ .../sdk/demo/compose/screens/ObjectsScreen.kt | 88 ++++++++++++++ .../demo/compose/screens/SnapshotScreen.kt | 104 +++++++++++++++++ .../sdk/demo/compose/screens/ThemeScreen.kt | 38 ++++++ .../drawable/ic_baseline_photo_camera_24.xml | 13 +++ gradle/libs.versions.toml | 18 +++ 27 files changed, 1155 insertions(+), 75 deletions(-) create mode 100644 app/src/main/java/ru/dgis/sdk/demo/ComposeActivity.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/CaptionSlider.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/EnumToggle.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/MapControls.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/MapViewModel.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/MarkerViewModel.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/RGBAImage.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightGravityConfigurator.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightMarginsConfigurator.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapFpsConfigurator.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapThemeConfigurator.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MarkerConfigurator.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/ComposeScreenData.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/ControlsScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/CopyrightScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/FpsScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/HomeScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/MarkersScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/ObjectsScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/SnapshotScreen.kt create mode 100644 app/src/main/java/ru/dgis/sdk/demo/compose/screens/ThemeScreen.kt create mode 100644 app/src/main/res/drawable/ic_baseline_photo_camera_24.xml diff --git a/app/build.gradle b/app/build.gradle index caa4cda..8ba072e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,6 +39,7 @@ android { buildFeatures { viewBinding true buildConfig true + compose true } splits { @@ -64,6 +65,9 @@ android { kotlinOptions { jvmTarget = "1.8" } + composeOptions { + kotlinCompilerExtensionVersion = libs.versions.compose.extension.get() + } applicationVariants.configureEach { it.compileConfiguration.resolutionStrategy { @@ -165,6 +169,7 @@ dependencies { releaseImplementation libs.firebase.crashlytics.ndk implementation libs.dgis.sdk + implementation libs.dgis.compose implementation libs.huawei.base implementation libs.huawei.location @@ -176,4 +181,15 @@ dependencies { classifier = "sources" } } + + // Compose + def composeBom = platform(libs.compose.bom) + implementation(composeBom) + implementation(libs.compose.foundational) + implementation(libs.compose.material3) + implementation(libs.compose.preview) + implementation(libs.compose.material) + debugImplementation(libs.compose.preview.debug) + implementation(libs.navigation.runtime) + implementation(libs.navigation.compose) } diff --git a/app/config/androidLint/baseline.xml b/app/config/androidLint/baseline.xml index db70fe7..c6d3e4e 100644 --- a/app/config/androidLint/baseline.xml +++ b/app/config/androidLint/baseline.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - @@ -1292,28 +1274,6 @@ file="src/main/res/drawable-xxhdpi"/> - - - - - - - - + () + + // Update the theme to match the system settings. + val theme = if (isSystemInDarkTheme()) { + MapTheme.defaultDarkTheme + } else { + MapTheme.defaultTheme + } + viewModel.state.setTheme(theme) + + HomeScreen( + mapState = viewModel.state + ) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt b/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt index dca77be..3f64685 100644 --- a/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt +++ b/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt @@ -26,6 +26,10 @@ class MainActivity : AppCompatActivity() { private val RECORD_REQUEST_CODE = 101 private val pages = listOf( + Page("Compose Examples") { + val intent = Intent(this@MainActivity, ComposeActivity::class.java) + startActivity(intent) + }, Page("Generic Map") { val intent = Intent(this@MainActivity, GenericMapActivity::class.java) startActivity(intent) diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/CaptionSlider.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/CaptionSlider.kt new file mode 100644 index 0000000..1cbc6ab --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/CaptionSlider.kt @@ -0,0 +1,20 @@ +package ru.dgis.sdk.demo.compose + +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable + +@Composable +fun CaptionSlider(caption: String, value: Int, onValueChange: (Int) -> Unit) { + Column { + Text(text = "$caption: $value") + androidx.compose.material3.Slider( + value = value.toFloat(), + onValueChange = { newValue -> + onValueChange(newValue.toInt()) + }, + valueRange = 0f..100f, + steps = 100 + ) + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/EnumToggle.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/EnumToggle.kt new file mode 100644 index 0000000..454ff5b --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/EnumToggle.kt @@ -0,0 +1,43 @@ +package ru.dgis.sdk.demo.compose + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +interface Displayable { + val displayName: String +} + +val T.displayName: String + get() = if (this is Displayable) this.displayName else this.toString() + +@Composable +inline fun > EnumToggle( + value: T, + isSelected: Boolean, + crossinline onSelected: (T) -> Unit +) { + Button( + onClick = { onSelected(value) }, + colors = ButtonDefaults.buttonColors( + containerColor = if (isSelected) { + MaterialTheme.colorScheme.primary + } else { + Color.LightGray + }, + contentColor = if (isSelected) { + Color.White + } else { + Color.Black + } + ), + shape = RoundedCornerShape(8.dp) + ) { + Text(text = value.displayName) + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/MapControls.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/MapControls.kt new file mode 100644 index 0000000..1148f75 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/MapControls.kt @@ -0,0 +1,58 @@ +package ru.dgis.sdk.demo.compose + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import ru.dgis.sdk.compose.map.controls.CompassComposable +import ru.dgis.sdk.compose.map.controls.IndoorComposable +import ru.dgis.sdk.compose.map.controls.MyLocationComposable +import ru.dgis.sdk.compose.map.controls.TrafficComposable +import ru.dgis.sdk.compose.map.controls.ZoomComposable +import ru.dgis.sdk.map.Map + +@Composable +fun MapControls(map: Map) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(5.dp), + contentAlignment = Alignment.CenterStart + ) { + Column( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 30.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + TrafficComposable(map) + } + Column( + modifier = Modifier.align(Alignment.CenterEnd), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + ZoomComposable(map) + } + Column( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(bottom = 30.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + CompassComposable(map) + MyLocationComposable(map) + } + Column( + modifier = Modifier.align(Alignment.CenterStart), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + IndoorComposable(map) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/MapViewModel.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/MapViewModel.kt new file mode 100644 index 0000000..3ed7a33 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/MapViewModel.kt @@ -0,0 +1,33 @@ +package ru.dgis.sdk.demo.compose + +import androidx.lifecycle.ViewModel +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.coordinates.Bearing +import ru.dgis.sdk.coordinates.GeoPoint +import ru.dgis.sdk.map.CameraPosition +import ru.dgis.sdk.map.MapOptions +import ru.dgis.sdk.map.Zoom + +private fun createMapOptions(): MapOptions { + // Position for testing controls such as Indoor и Compass. + val cameraPosition = CameraPosition( + point = GeoPoint( + latitude = 55.760898, + longitude = 37.620242 + ), + bearing = Bearing(20.0), + zoom = Zoom(17f) + ) + + return MapOptions().apply { + position = cameraPosition + } +} + +class MapViewModel() : ViewModel() { + val state by lazy { + MapComposableState( + mapOptions = createMapOptions() + ) + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/MarkerViewModel.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/MarkerViewModel.kt new file mode 100644 index 0000000..bee2921 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/MarkerViewModel.kt @@ -0,0 +1,42 @@ +package ru.dgis.sdk.demo.compose + +import androidx.compose.ui.text.input.TextFieldValue +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import ru.dgis.sdk.map.LogicalPixel +import ru.dgis.sdk.map.Marker +import ru.dgis.sdk.map.TextHorizontalAlignment + +class MarkerViewModel(private val marker: Marker) : ViewModel() { + private var _text = MutableStateFlow(TextFieldValue(marker.text)) + val text = _text.asStateFlow() + + fun setText(text: TextFieldValue) { + marker.text = text.text + _text.value = text + } + + private var _textStyle = MutableStateFlow(marker.textStyle) + val textStyle = _textStyle.asStateFlow() + + fun setHorizontalAlignment(alignment: TextHorizontalAlignment) { + _textStyle.value = _textStyle.value.copy(textHorizontalAlignment = alignment) + marker.textStyle = _textStyle.value + } + + fun setFontSize(size: LogicalPixel) { + _textStyle.value = _textStyle.value.copy(fontSize = size) + marker.textStyle = _textStyle.value + } + + fun setStrokeWidth(width: LogicalPixel) { + _textStyle.value = _textStyle.value.copy(strokeWidth = width) + marker.textStyle = _textStyle.value + } + + fun setOffset(offset: LogicalPixel) { + _textStyle.value = _textStyle.value.copy(textOffset = offset) + marker.textStyle = _textStyle.value + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/RGBAImage.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/RGBAImage.kt new file mode 100644 index 0000000..6cb0811 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/RGBAImage.kt @@ -0,0 +1,21 @@ +package ru.dgis.sdk.demo.compose + +import android.graphics.Bitmap +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.asImageBitmap +import java.nio.ByteBuffer + +@Composable +fun RGBAImage(byteArray: ByteArray, width: Int, height: Int) { + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + val buffer = ByteBuffer.wrap(byteArray) + bitmap.copyPixelsFromBuffer(buffer) + + val imageBitmap = bitmap.asImageBitmap() + + Image( + bitmap = imageBitmap, + contentDescription = "RGBA_8888 Image" + ) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightGravityConfigurator.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightGravityConfigurator.kt new file mode 100644 index 0000000..54046bd --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightGravityConfigurator.kt @@ -0,0 +1,45 @@ +package ru.dgis.sdk.demo.compose.configurators + +import android.view.Gravity +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.dp +import ru.dgis.sdk.demo.compose.Displayable +import ru.dgis.sdk.demo.compose.EnumToggle + +private fun CopyrightGravity.toMapCopyrightGravity(): Int { + return when (this) { + CopyrightGravity.TOP_LEFT -> Gravity.TOP or Gravity.START + CopyrightGravity.TOP_RIGHT -> Gravity.TOP or Gravity.END + CopyrightGravity.BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.START + CopyrightGravity.BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.END + } +} + +private enum class CopyrightGravity(override val displayName: String) : Displayable { + TOP_LEFT("\u2196"), + TOP_RIGHT("\u2197"), + BOTTOM_LEFT("\u2199"), + BOTTOM_RIGHT("\u2198") +} + +@Composable +fun MapCopyrightGravityConfigurator( + gravity: Int, + onGravityChange: (Int) -> Unit +) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + enumValues().forEach { option -> + EnumToggle( + value = option, + isSelected = gravity == option.toMapCopyrightGravity(), + onSelected = { + onGravityChange(it.toMapCopyrightGravity()) + } + ) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightMarginsConfigurator.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightMarginsConfigurator.kt new file mode 100644 index 0000000..98e0ce1 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapCopyrightMarginsConfigurator.kt @@ -0,0 +1,27 @@ +package ru.dgis.sdk.demo.compose.configurators + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import ru.dgis.sdk.compose.map.CopyrightMargins +import ru.dgis.sdk.demo.compose.CaptionSlider + +@Composable +fun MapCopyrightMarginsConfigurator( + margins: CopyrightMargins, + onMarginsChange: ((CopyrightMargins) -> Unit)? = null +) { + Column { + CaptionSlider("Left", margins.left) { newLeft -> + onMarginsChange?.invoke(margins.copy(left = newLeft)) + } + CaptionSlider("Top", margins.top) { newTop -> + onMarginsChange?.invoke(margins.copy(top = newTop)) + } + CaptionSlider("Right", margins.right) { newRight -> + onMarginsChange?.invoke(margins.copy(right = newRight)) + } + CaptionSlider("Bottom", margins.bottom) { newBottom -> + onMarginsChange?.invoke(margins.copy(bottom = newBottom)) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapFpsConfigurator.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapFpsConfigurator.kt new file mode 100644 index 0000000..5035b26 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapFpsConfigurator.kt @@ -0,0 +1,57 @@ +package ru.dgis.sdk.demo.compose.configurators + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Checkbox +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import ru.dgis.sdk.demo.compose.CaptionSlider +import ru.dgis.sdk.map.Fps + +@Composable +private fun MapFpsSlider(label: String, value: Fps?, onValueChange: (Fps?) -> Unit) { + var sliderValue by remember { mutableIntStateOf(value?.value ?: 0) } + + Row { + Checkbox( + checked = value != null, + onCheckedChange = { onValueChange(if (it) Fps(sliderValue) else null) } + ) + + CaptionSlider( + caption = label, + value = sliderValue, + onValueChange = { + sliderValue = it + if (value != null) { + onValueChange(Fps(it)) + } + } + ) + } +} + +@Composable +fun MapFpsConfigurator( + maxFpsValue: Fps?, + onMaxFpsChange: (Fps?) -> Unit, + powerSavingMaxFpsValue: Fps?, + onPowerSavingMaxFpsChange: (Fps?) -> Unit +) { + Column { + MapFpsSlider( + label = "MapFps", + value = maxFpsValue, + onValueChange = onMaxFpsChange + ) + + MapFpsSlider( + label = "PowerSavingMapFps", + value = powerSavingMaxFpsValue, + onValueChange = onPowerSavingMaxFpsChange + ) + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapThemeConfigurator.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapThemeConfigurator.kt new file mode 100644 index 0000000..80af747 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MapThemeConfigurator.kt @@ -0,0 +1,37 @@ +package ru.dgis.sdk.demo.compose.configurators + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import ru.dgis.sdk.demo.compose.Displayable +import ru.dgis.sdk.demo.compose.EnumToggle +import ru.dgis.sdk.map.MapTheme + +private enum class Theme(override val displayName: String) : Displayable { + LIGHT("Light"), + DARK("Dark") +} + +private fun Theme.toMapTheme(): MapTheme { + return when (this) { + Theme.LIGHT -> MapTheme.defaultTheme + Theme.DARK -> MapTheme.defaultDarkTheme + } +} + +@Composable +fun MapThemeConfigurator( + theme: MapTheme, + onThemeChange: (MapTheme) -> Unit +) { + Column { + enumValues().forEach { option -> + EnumToggle( + value = option, + isSelected = option.toMapTheme() == theme, + onSelected = { + onThemeChange(it.toMapTheme()) + } + ) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MarkerConfigurator.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MarkerConfigurator.kt new file mode 100644 index 0000000..829a497 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/configurators/MarkerConfigurator.kt @@ -0,0 +1,82 @@ +package ru.dgis.sdk.demo.compose.configurators + +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.material3.Card +import androidx.compose.material3.OutlinedTextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import ru.dgis.sdk.demo.compose.CaptionSlider +import ru.dgis.sdk.demo.compose.EnumToggle +import ru.dgis.sdk.demo.compose.MarkerViewModel +import ru.dgis.sdk.map.LogicalPixel +import ru.dgis.sdk.map.TextHorizontalAlignment + +@Composable +fun MarkerConfigurator(markerViewModel: MarkerViewModel, modifier: Modifier = Modifier) { + val text by markerViewModel.text.collectAsState() + val textStyle by markerViewModel.textStyle.collectAsState() + + Card(modifier = modifier) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp) + ) { + CaptionSlider( + caption = "Font Size", + value = textStyle.fontSize.value.toInt(), + onValueChange = { + markerViewModel.setFontSize(LogicalPixel(it.toFloat())) + } + ) + + CaptionSlider( + caption = "Text Offset", + value = textStyle.textOffset.value.toInt(), + onValueChange = { + markerViewModel.setOffset(LogicalPixel(it.toFloat())) + } + ) + + CaptionSlider( + caption = "Stroke Width", + value = textStyle.strokeWidth.value.toInt(), + onValueChange = { + markerViewModel.setStrokeWidth(LogicalPixel(it.toFloat())) + } + ) + + OutlinedTextField( + value = text, + onValueChange = { markerViewModel.setText(it) }, + modifier = Modifier.fillMaxWidth(), + maxLines = 2, + singleLine = false + ) + + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(top = 10.dp) + ) { + enumValues().forEach { option -> + EnumToggle( + value = option, + isSelected = option == textStyle.textHorizontalAlignment, + onSelected = { + markerViewModel.setHorizontalAlignment(it) + } + ) + } + } + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ComposeScreenData.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ComposeScreenData.kt new file mode 100644 index 0000000..ceb7712 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ComposeScreenData.kt @@ -0,0 +1,11 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.runtime.Composable +import androidx.lifecycle.ViewModelStoreOwner +import ru.dgis.sdk.compose.map.MapComposableState + +data class ComposeScreenData( + val id: String, + val title: String, + val content: @Composable (MapComposableState, ViewModelStoreOwner) -> Unit +) diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ControlsScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ControlsScreen.kt new file mode 100644 index 0000000..9f84e10 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ControlsScreen.kt @@ -0,0 +1,30 @@ +package ru.dgis.sdk.demo.compose.screens + +import android.util.Log +import android.view.Gravity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.demo.compose.MapControls + +@Composable +fun ControlsScreen(mapState: MapComposableState) { + val map by mapState.map.collectAsState() + + LaunchedEffect(mapState) { + // Display copyright at the bottom left corner to avoid conflict with the MyLocation control. + mapState.setCopyrightGravity(Gravity.BOTTOM or Gravity.START) + + // Just check custom uri opener. + mapState.setCopyrightUriOpener { Log.d("ControlsScreen", it) } + } + + MapComposable(state = mapState) + + map?.let { + MapControls(map = it) + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/CopyrightScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/CopyrightScreen.kt new file mode 100644 index 0000000..30cef12 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/CopyrightScreen.kt @@ -0,0 +1,61 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.demo.compose.configurators.MapCopyrightGravityConfigurator +import ru.dgis.sdk.demo.compose.configurators.MapCopyrightMarginsConfigurator +import ru.dgis.sdk.map.MapOptions + +@Composable +fun CopyrightScreen(mapState: MapComposableState) { + MapComposable(state = mapState) + + Box( + modifier = Modifier + .fillMaxSize() + .padding(10.dp) + .padding(bottom = 20.dp), + contentAlignment = Alignment.BottomCenter + ) { + Column { + MapCopyrightMarginsConfigurator( + margins = mapState.copyrightMargins.collectAsState().value, + onMarginsChange = { mapState.setCopyrightMargins(it) } + ) + + Row( + verticalAlignment = Alignment.CenterVertically + ) { + MapCopyrightGravityConfigurator( + gravity = mapState.copyrightGravity.collectAsState().value, + onGravityChange = { mapState.setCopyrightGravity(it) } + ) + + Checkbox( + checked = mapState.showApiVersionInCopyright.collectAsState().value, + onCheckedChange = { mapState.setShowApiVersionInCopyright(it) } + ) + Text(text = "Version") + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun CopyrightScreenPreview() { + CopyrightScreen(mapState = MapComposableState(MapOptions())) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/FpsScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/FpsScreen.kt new file mode 100644 index 0000000..a128e8b --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/FpsScreen.kt @@ -0,0 +1,66 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.demo.compose.configurators.MapFpsConfigurator +import ru.dgis.sdk.map.MapOptions + +@Composable +fun FpsScreen(mapState: MapComposableState) { + var fpsCounter by remember { mutableIntStateOf(0) } + + LaunchedEffect(mapState) { + mapState.fpsCounterCallback = { fpsCounter = it.value } + } + + MapComposable(state = mapState) + + Box( + modifier = Modifier + .fillMaxSize() + .padding(5.dp) + .padding(bottom = 20.dp), + contentAlignment = Alignment.BottomCenter + ) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + MapFpsConfigurator( + maxFpsValue = mapState.maxFps.collectAsState().value, + onMaxFpsChange = { mapState.setMaxFps(it) }, + powerSavingMaxFpsValue = mapState.powerSavingMaxFps.collectAsState().value, + onPowerSavingMaxFpsChange = { mapState.setPowerSavingMaxFps(it) } + ) + Text( + modifier = Modifier.fillMaxWidth(), + text = "FPS: $fpsCounter", + textAlign = TextAlign.Center, + fontSize = 20.sp + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun FpsScreenPreview() { + FpsScreen(mapState = MapComposableState(MapOptions())) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/HomeScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/HomeScreen.kt new file mode 100644 index 0000000..8c2aff3 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/HomeScreen.kt @@ -0,0 +1,92 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.map.MapOptions + +@Composable +private fun HomeButton(text: String, onClick: () -> Unit) { + Button( + onClick = onClick, + modifier = Modifier + .fillMaxWidth() + .height(140.dp), + shape = RoundedCornerShape(8.dp) + ) { + Text(text, fontSize = 16.sp) + } +} + +@Composable +private fun HomeScreen(navController: NavController) { + LazyVerticalGrid( + columns = GridCells.Fixed(2), + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + item { + HomeButton(text = "Theme", onClick = { navController.navigate("theme") }) + } + item { + HomeButton(text = "Copyright", onClick = { navController.navigate("copyright") }) + } + item { + HomeButton(text = "Fps", onClick = { navController.navigate("fps") }) + } + item { + HomeButton(text = "Objects", onClick = { navController.navigate("objects") }) + } + item { + HomeButton(text = "Snapshot", onClick = { navController.navigate("snapshot") }) + } + item { + HomeButton(text = "Map Controls", onClick = { navController.navigate("controls") }) + } + item { + HomeButton(text = "Markers", onClick = { navController.navigate("markers") }) + } + } +} + +@Composable +fun HomeScreen(mapState: MapComposableState) { + val navController = rememberNavController() + NavHost(navController, startDestination = "home") { + composable("home") { HomeScreen(navController) } + composable("theme") { ThemeScreen(mapState = mapState) } + composable("copyright") { CopyrightScreen(mapState = mapState) } + composable("fps") { FpsScreen(mapState = mapState) } + composable("objects") { ObjectsScreen(mapState = mapState) } + composable("snapshot") { SnapshotScreen(mapState = mapState) } + composable("controls") { ControlsScreen(mapState = mapState) } + composable("markers") { MarkersScreen(mapState = mapState) } + } +} + +@Preview(showBackground = true) +@Composable +fun HomeScreenPreview() { + HomeScreen( + mapState = MapComposableState(MapOptions()) + ) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/MarkersScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/MarkersScreen.kt new file mode 100644 index 0000000..7d49b20 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/MarkersScreen.kt @@ -0,0 +1,77 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import ru.dgis.sdk.DGis +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.coordinates.GeoPoint +import ru.dgis.sdk.demo.R +import ru.dgis.sdk.demo.compose.MarkerViewModel +import ru.dgis.sdk.demo.compose.configurators.MarkerConfigurator +import ru.dgis.sdk.geometry.GeoPointWithElevation +import ru.dgis.sdk.map.Map +import ru.dgis.sdk.map.MapObjectManager +import ru.dgis.sdk.map.Marker +import ru.dgis.sdk.map.MarkerOptions +import ru.dgis.sdk.map.imageFromResource + +private fun createMarker(position: GeoPoint): Marker { + val options = MarkerOptions( + position = GeoPointWithElevation(point = position), + icon = imageFromResource(DGis.context(), R.drawable.ic_marker) + ) + + return Marker(options).apply { + text = "Text text text\nText text" + } +} + +@Composable +private fun Marker(map: Map, modifier: Modifier = Modifier) { + data class State( + val marker: Marker, + val mapObjectManager: MapObjectManager + ) + + val state = remember { + val marker = createMarker(map.camera.position.point) + + val mapObjectManager = MapObjectManager(map).apply { + addObject(marker) + } + + State(marker, mapObjectManager) + } + + DisposableEffect(state) { + onDispose { + state.mapObjectManager.removeObject(state.marker) + } + } + + MarkerConfigurator( + modifier = modifier, + markerViewModel = MarkerViewModel(state.marker) + ) +} + +@Composable +fun MarkersScreen(mapState: MapComposableState) { + val map by mapState.map.collectAsState() + + Box(modifier = Modifier.fillMaxSize()) { + MapComposable(state = mapState) + + map?.let { + Marker(map = it, modifier = Modifier.align(Alignment.BottomCenter)) + } + } +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ObjectsScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ObjectsScreen.kt new file mode 100644 index 0000000..373f387 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ObjectsScreen.kt @@ -0,0 +1,88 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ModalBottomSheetLayout +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.map.MapOptions +import ru.dgis.sdk.map.RenderedObjectInfo + +@Composable +private fun ObjectCard(mapObject: RenderedObjectInfo?, onClose: () -> Unit) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = "Some object", style = MaterialTheme.typography.headlineSmall) + Text(text = "$mapObject", modifier = Modifier.padding(top = 10.dp)) + Spacer(modifier = Modifier.height(8.dp)) + Button(onClick = onClose) { + Text("Close") + } + } + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun ObjectsScreen(mapState: MapComposableState) { + var selectedObject by remember { mutableStateOf(null) } + val coroutineScope = rememberCoroutineScope() + val bottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) + + LaunchedEffect(mapState) { + mapState.objectTappedCallback = { + selectedObject = it + coroutineScope.launch { + bottomSheetState.show() + } + } + } + + ModalBottomSheetLayout( + sheetState = bottomSheetState, + sheetContent = { + ObjectCard(selectedObject) { + coroutineScope.launch { + bottomSheetState.hide() + } + } + } + ) { + MapComposable(state = mapState) + } +} + +@Preview(showBackground = true) +@Composable +fun ObjectsScreenPreview() { + ObjectsScreen(mapState = MapComposableState(MapOptions())) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/SnapshotScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/SnapshotScreen.kt new file mode 100644 index 0000000..c73bf28 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/SnapshotScreen.kt @@ -0,0 +1,104 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.demo.R +import ru.dgis.sdk.demo.compose.RGBAImage +import ru.dgis.sdk.map.ImageData +import ru.dgis.sdk.map.MapOptions + +@Composable +private fun SnapshotDialog(imageData: ImageData, onDismiss: () -> Unit) { + Box( + modifier = Modifier + .fillMaxSize() + .background(Color.Black.copy(alpha = 0.5f)) + .clickable { onDismiss() }, + contentAlignment = Alignment.Center + ) { + Dialog(onDismissRequest = { onDismiss() }) { + Column { + RGBAImage(imageData.data, imageData.size.width, imageData.size.height) + Button(onClick = { onDismiss() }) { + Text(text = "Close") + } + } + } + } +} + +@Composable +private fun SnapshotButton(enabled: Boolean, onClick: () -> Unit) { + FilledIconButton( + onClick = { onClick() }, + enabled = enabled, + modifier = Modifier + .padding(5.dp) + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_baseline_photo_camera_24), + contentDescription = null + ) + } +} + +@Composable +fun SnapshotScreen(mapState: MapComposableState) { + var snapshot by remember { mutableStateOf(null) } + + MapComposable(state = mapState) + + Box( + contentAlignment = Alignment.BottomCenter, + modifier = Modifier + .fillMaxSize() + ) { + SnapshotButton( + enabled = mapState.map.collectAsState().value != null, + onClick = { + mapState.takeSnapshot()?.let { it -> + it.onComplete( + resultCallback = { snapshot = it }, + errorCallback = {} + ) + } + } + ) + } + + snapshot?.let { + SnapshotDialog(it) { + snapshot = null + } + } +} + +@Preview(showBackground = true) +@Composable +fun ScreenshotScreenPreview() { + SnapshotScreen(mapState = MapComposableState(MapOptions())) +} diff --git a/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ThemeScreen.kt b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ThemeScreen.kt new file mode 100644 index 0000000..fc126d7 --- /dev/null +++ b/app/src/main/java/ru/dgis/sdk/demo/compose/screens/ThemeScreen.kt @@ -0,0 +1,38 @@ +package ru.dgis.sdk.demo.compose.screens + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import ru.dgis.sdk.compose.map.MapComposable +import ru.dgis.sdk.compose.map.MapComposableState +import ru.dgis.sdk.demo.compose.configurators.MapThemeConfigurator +import ru.dgis.sdk.map.MapOptions + +@Composable +fun ThemeScreen(mapState: MapComposableState) { + MapComposable(state = mapState) + + Box( + modifier = Modifier + .fillMaxSize() + .padding(start = 5.dp), + contentAlignment = Alignment.CenterStart + ) { + MapThemeConfigurator( + theme = mapState.theme.collectAsState().value, + onThemeChange = { mapState.setTheme(it) } + ) + } +} + +@Preview(showBackground = true) +@Composable +fun ThemeScreenPreview() { + ThemeScreen(mapState = MapComposableState(MapOptions())) +} diff --git a/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml b/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml new file mode 100644 index 0000000..13186de --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_photo_camera_24.xml @@ -0,0 +1,13 @@ + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce91319..3f62e0f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,12 @@ taskTree = "2.1.1" undercouch-download = "5.4.0" ktlint = "11.5.0" +compose-extension = "1.5.14" +compose-bom = "2024.12.01" +compose-material3 = "1.3.1" + dgis-sdk = "12.3.0" +dgis-compose = "1.0.0" appcompat = "1.6.1" constraintlayout = "2.1.4" @@ -25,6 +30,8 @@ huawei-base = "6.11.0.302" huawei-location = "6.12.0.300" activity = "1.8.0" +navigation = "2.8.5" + [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } @@ -54,8 +61,19 @@ material = { module = "com.google.android.material:material", version.ref = "mat retrofuture = { module = "net.sourceforge.streamsupport:android-retrofuture", version.ref = "retrofuture" } dgis-sdk = { module = "ru.dgis.sdk:sdk-full", version.ref = "dgis-sdk" } +dgis-compose = { module = "ru.dgis.sdk:compose-map", version.ref = "dgis-compose" } huawei-base = { module = "com.huawei.hms:base", version.ref = "huawei-base" } huawei-location = { module= "com.huawei.hms:location", version.ref = "huawei-location" } androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" } +compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" } +compose-foundational = { module = "androidx.compose.foundation:foundation" } +compose-material = { module = "androidx.compose.material:material" } +compose-material3 = { module = "androidx.compose.material3:material3-android", version.ref = "compose-material3" } +compose-preview = { module = "androidx.compose.ui:ui-tooling-preview" } +compose-preview-debug = { module = "androidx.compose.ui:ui-tooling" } + +navigation-runtime = { module = "androidx.navigation:navigation-runtime-ktx", version.ref = "navigation" } +navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation" } +