diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e0fa651c7..ddfd0ebd5 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -83,6 +83,7 @@ dependencies {
implementation(projects.features.setting.alerts)
implementation(projects.features.setting.appearance)
+ implementation(projects.features.setting.debugTools)
implementation(projects.features.setting.faq)
implementation(projects.features.setting.map)
implementation(projects.features.setting.whatsnew)
diff --git a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponent.kt b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponent.kt
index 429be9b95..ffc82bd61 100644
--- a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponent.kt
+++ b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponent.kt
@@ -31,6 +31,7 @@ interface RoadsRootComponent {
sealed class Child {
data class Main(val component: MainComponent) : Child()
data class Appearance(val appearanceComponent: AppearanceComponent) : Child()
+ data object DebugTools : Child()
data class Map(val mapSettingsComponent: MapSettingsComponent) : Child()
data class Alerts(val alertsComponent: AlertsComponent) : Child()
data class WhatsNew(val whatsNewComponent: WhatsNewComponent) : Child()
diff --git a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponentImpl.kt b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponentImpl.kt
index a86c06312..f673002c1 100644
--- a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponentImpl.kt
+++ b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RoadsRootComponentImpl.kt
@@ -60,6 +60,7 @@ class RoadsRootComponentImpl(
override fun open(page: Page) {
when (page) {
Page.Appearance -> navigation.push(Config.Appearance)
+ Page.DebugTools -> navigation.push(Config.DebugTools)
Page.Map -> navigation.push(Config.MapSettings)
Page.Alerts -> navigation.push(Config.Alerts)
Page.WhatsNew -> navigation.push(Config.WhatsNew)
@@ -77,21 +78,18 @@ class RoadsRootComponentImpl(
onOpen = ::open
)
)
-
is Config.Appearance -> Child.Appearance(
appearanceComponent = buildAppearanceComponent(componentContext)
)
-
+ is Config.DebugTools -> Child.DebugTools
is Config.Alerts -> Child.Alerts(alertsComponent = buildAlertsComponent(componentContext))
is Config.MapSettings -> Child.Map(
mapSettingsComponent = buildMapSettingsComponent(componentContext)
)
-
is Config.NextFeatures -> TODO()
is Config.WhatsNew -> Child.WhatsNew(
whatsNewComponent = buildWhatsNewComponent(componentContext)
)
-
is Config.FAQ -> Child.FAQ(faqComponent = buildFaqComponent(componentContext))
}
@@ -102,6 +100,9 @@ class RoadsRootComponentImpl(
@Parcelize
data object Appearance : Config()
+ @Parcelize
+ data object DebugTools : Config()
+
@Parcelize
data object MapSettings : Config()
diff --git a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RootContent.kt b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RootContent.kt
index 967f77808..b363e5d1f 100644
--- a/app/src/main/java/com/egoriku/grodnoroads/screen/root/RootContent.kt
+++ b/app/src/main/java/com/egoriku/grodnoroads/screen/root/RootContent.kt
@@ -15,6 +15,7 @@ import com.egoriku.grodnoroads.screen.root.store.headlamp.HeadLampType
import com.egoriku.grodnoroads.screen.root.ui.HeadLampDialog
import com.egoriku.grodnoroads.setting.alerts.AlertsScreen
import com.egoriku.grodnoroads.setting.appearance.screen.AppearanceScreen
+import com.egoriku.grodnoroads.setting.debugtools.DebugToolsScreen
import com.egoriku.grodnoroads.setting.faq.screen.FaqScreen
import com.egoriku.grodnoroads.setting.map.MapSettingsScreen
import com.egoriku.grodnoroads.setting.whatsnew.screen.WhatsNewScreen
@@ -44,6 +45,9 @@ fun RootContent(roadsRootComponent: RoadsRootComponent) {
) {
when (val child = it.instance) {
is Child.Main -> MainUi(component = child.component)
+ is Child.DebugTools -> DebugToolsScreen(
+ onBack = roadsRootComponent::onBack
+ )
is Child.Appearance -> AppearanceScreen(
appearanceComponent = child.appearanceComponent,
onBack = roadsRootComponent::onBack
diff --git a/features/setting/debugTools/.gitignore b/features/setting/debugTools/.gitignore
new file mode 100644
index 000000000..42afabfd2
--- /dev/null
+++ b/features/setting/debugTools/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/features/setting/debugTools/build.gradle.kts b/features/setting/debugTools/build.gradle.kts
new file mode 100644
index 000000000..cafcfea45
--- /dev/null
+++ b/features/setting/debugTools/build.gradle.kts
@@ -0,0 +1,26 @@
+plugins {
+ id("grodnoroads.library")
+ id("grodnoroads.library.compose")
+}
+
+android {
+ namespace = "com.egoriku.grodnoroads.setting.debugtools"
+}
+
+dependencies {
+ implementation(projects.libraries.foundation)
+ implementation(projects.libraries.maps.core)
+ implementation(projects.libraries.maps.compose)
+
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.compose.foundation)
+ implementation(libs.androidx.compose.material3)
+ implementation(libs.androidx.compose.material.icons)
+ implementation(libs.androidx.compose.ui.tooling.preview)
+ debugImplementation(libs.androidx.compose.ui.tooling)
+
+ implementation(libs.immutable.collections)
+ implementation(libs.google.maps)
+ implementation(libs.google.maps)
+
+}
\ No newline at end of file
diff --git a/features/setting/debugTools/consumer-rules.pro b/features/setting/debugTools/consumer-rules.pro
new file mode 100644
index 000000000..e69de29bb
diff --git a/features/setting/debugTools/src/main/AndroidManifest.xml b/features/setting/debugTools/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..568741e54
--- /dev/null
+++ b/features/setting/debugTools/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/DebugToolsScreen.kt b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/DebugToolsScreen.kt
new file mode 100644
index 000000000..9169e5ed1
--- /dev/null
+++ b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/DebugToolsScreen.kt
@@ -0,0 +1,173 @@
+package com.egoriku.grodnoroads.setting.debugtools
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Remove
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import com.egoriku.grodnoroads.foundation.core.rememberMutableState
+import com.egoriku.grodnoroads.maps.compose.GoogleMap
+import com.egoriku.grodnoroads.maps.compose.MapUpdater
+import com.egoriku.grodnoroads.maps.compose.impl.onMapScope
+import com.egoriku.grodnoroads.maps.core.StableLatLng
+import com.egoriku.grodnoroads.setting.debugtools.data.PolylineRepository
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.maps.android.ktx.model.cameraPosition
+import kotlinx.collections.immutable.PersistentList
+import kotlinx.collections.immutable.persistentListOf
+
+internal sealed interface State {
+ data object None : State
+ data class Loaded(val polyline: PersistentList) : State
+}
+
+internal data class Polyline(
+ val name: String,
+ val points: PersistentList,
+ val bounds: PersistentList = calculateBounds(points)
+) {
+ fun copy(points: PersistentList) = Polyline(
+ name = name,
+ points = points,
+ bounds = calculateBounds(points)
+ )
+}
+
+private fun calculateBounds(points: PersistentList): PersistentList {
+ val builder = LatLngBounds.builder()
+ for (latLng in points) {
+ builder.include(latLng)
+ }
+ val pointsBounds = builder.build()
+
+ return persistentListOf(
+ StableLatLng(
+ pointsBounds.northeast.latitude,
+ pointsBounds.northeast.longitude
+ ),
+ StableLatLng(
+ pointsBounds.southwest.latitude,
+ pointsBounds.northeast.longitude
+ ),
+ StableLatLng(
+ pointsBounds.southwest.latitude,
+ pointsBounds.southwest.longitude
+ ),
+ StableLatLng(
+ pointsBounds.northeast.latitude,
+ pointsBounds.southwest.longitude
+ )
+ )
+}
+
+@Composable
+fun DebugToolsScreen(onBack: () -> Unit) {
+ val repository = remember { PolylineRepository() }
+
+ Surface {
+ val state by repository.polylines.collectAsState()
+
+ var isMapLoaded by rememberMutableState { false }
+ var mapUpdater by rememberMutableState { null }
+
+ Box(modifier = Modifier.fillMaxSize()) {
+ GoogleMap(
+ contentPadding = WindowInsets.navigationBars.asPaddingValues(),
+ cameraPositionProvider = {
+ cameraPosition {
+ target(LatLng(53.6687765, 23.8212226))
+ zoom(12.5f)
+ }
+ },
+ onMapLoaded = { isMapLoaded = true },
+ onMapUpdaterChanged = { mapUpdater = it }
+ )
+ TopActions(
+ modifier = Modifier.align(Alignment.TopStart),
+ onBack = onBack,
+ zoomIn = {
+ mapUpdater.onMapScope {
+ zoomIn()
+ }
+ },
+ zoomOut = {
+ mapUpdater.onMapScope {
+ zoomOut()
+ }
+ }
+ )
+ }
+
+ mapUpdater.onMapScope {
+ when (val state = state) {
+ is State.Loaded -> {
+ state.polyline.forEach {
+ PolygonMarker(
+ polyline = it,
+ onPointsChanged = repository::onPointsChanged
+ )
+ }
+ }
+ State.None -> {}
+ }
+ }
+ }
+}
+
+@Composable
+private fun TopActions(
+ modifier: Modifier,
+ onBack: () -> Unit,
+ zoomIn: () -> Unit,
+ zoomOut: () -> Unit,
+) {
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .statusBarsPadding()
+ .padding(horizontal = 16.dp)
+ ) {
+ Row {
+ FilledIconButton(
+ colors = IconButtonDefaults.filledIconButtonColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ ),
+ onClick = onBack
+ ) {
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = null
+ )
+ }
+ Spacer(modifier = Modifier.weight(1f))
+ }
+ FilledIconButton(
+ colors = IconButtonDefaults.filledIconButtonColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ ),
+ onClick = zoomIn
+ ) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = null
+ )
+ }
+ FilledIconButton(
+ colors = IconButtonDefaults.filledIconButtonColors(
+ containerColor = MaterialTheme.colorScheme.surface
+ ),
+ onClick = zoomOut
+ ) {
+ Icon(
+ imageVector = Icons.Default.Remove,
+ contentDescription = null
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/PolygonMarker.kt b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/PolygonMarker.kt
new file mode 100644
index 000000000..e0e808728
--- /dev/null
+++ b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/PolygonMarker.kt
@@ -0,0 +1,79 @@
+package com.egoriku.grodnoroads.setting.debugtools
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import com.egoriku.grodnoroads.foundation.core.rememberMutableState
+import com.egoriku.grodnoroads.maps.compose.MapUpdater
+import com.egoriku.grodnoroads.maps.compose.rememberDraggableMarker
+import com.egoriku.grodnoroads.maps.compose.rememberPolygon
+import com.egoriku.grodnoroads.maps.core.asStable
+import com.google.android.gms.maps.model.LatLng
+import com.google.maps.android.ktx.model.markerOptions
+import com.google.maps.android.ktx.model.polygonOptions
+import kotlinx.collections.immutable.PersistentList
+import kotlinx.collections.immutable.mutate
+
+context(MapUpdater)
+@Composable
+internal fun PolygonMarker(
+ polyline: Polyline,
+ onPointsChanged: (String, PersistentList) -> Unit
+) {
+ var points by rememberMutableState { polyline.points }
+ val boundsPolygon = rememberPolygon(
+ tag = "bounds_${polyline.name}",
+ polygonOptions = {
+ polygonOptions {
+ addAll(polyline.bounds.map { it.value })
+ strokeColor(Color.Red.toArgb())
+ }
+ }
+ )
+
+ val polygon = rememberPolygon(
+ tag = polyline.name,
+ polygonOptions = {
+ polygonOptions {
+ clickable(true)
+ addAll(polyline.points)
+ }
+ }
+ )
+
+ LaunchedEffect(points) {
+ polygon?.points = points
+ }
+
+ LaunchedEffect(polyline) {
+ boundsPolygon?.points = polyline.bounds.map { it.value }
+ }
+
+ polyline.points.forEachIndexed { index, it ->
+ var position by rememberMutableState(it) { it.asStable() }
+ val marker = rememberDraggableMarker(
+ tag = "${polyline.name}_bounds_$index",
+ markerOptions = {
+ markerOptions {
+ position(it)
+ draggable(true)
+ }
+ },
+ onPositionChange = { newPosition ->
+ val newPoints = polyline.points.mutate {
+ it[index] = newPosition.value
+ }
+ onPointsChanged(polyline.name, newPoints)
+ points = newPoints
+ position = newPosition
+ }
+ )
+
+ LaunchedEffect(position) {
+ marker?.position = position.value
+ }
+ }
+}
\ No newline at end of file
diff --git a/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/data/PolylineRepository.kt b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/data/PolylineRepository.kt
new file mode 100644
index 000000000..55e9232ba
--- /dev/null
+++ b/features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/data/PolylineRepository.kt
@@ -0,0 +1,144 @@
+package com.egoriku.grodnoroads.setting.debugtools.data
+
+import com.egoriku.grodnoroads.setting.debugtools.Polyline
+import com.egoriku.grodnoroads.setting.debugtools.State
+import com.egoriku.grodnoroads.setting.debugtools.State.Loaded
+import com.egoriku.grodnoroads.setting.debugtools.State.None
+import com.google.android.gms.maps.model.LatLng
+import kotlinx.collections.immutable.PersistentList
+import kotlinx.collections.immutable.persistentListOf
+import kotlinx.collections.immutable.toPersistentList
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+internal class PolylineRepository {
+
+ private val _polylines = MutableStateFlow(None)
+ val polylines = _polylines.asStateFlow()
+
+ init {
+ _polylines.tryEmit(
+ Loaded(
+ polyline = persistentListOf(
+ Polyline(
+ name = "grodno",
+ points = persistentListOf(
+ LatLng(53.677865, 23.7364759),
+ LatLng(53.677258, 23.7517825),
+ LatLng(53.6958691, 23.7523566),
+ LatLng(53.7137621, 23.7905214),
+ LatLng(53.7418072, 23.8205625),
+ LatLng(53.7228865, 23.9034438),
+ LatLng(53.6988061, 23.8997554),
+ LatLng(53.6840719, 23.8965026),
+ LatLng(53.6676634, 23.8967543),
+ LatLng(53.6483532, 23.8946692),
+ LatLng(53.6165424, 23.8885688),
+ LatLng(53.5998739, 23.8418484),
+ LatLng(53.6110952, 23.7947563),
+ LatLng(53.6160009, 23.7492421),
+ LatLng(53.6308808, 23.741084),
+ LatLng(53.6543211, 23.7315001),
+ LatLng(53.6689345, 23.7294389),
+ LatLng(53.6777983, 23.7256919),
+ LatLng(53.677865, 23.7364759)
+ )
+ ),
+ Polyline(
+ name = "berestovitca",
+ points = persistentListOf(
+ LatLng(53.1951594, 24.0030663),
+ LatLng(53.1884433, 24.0055632),
+ LatLng(53.1815879, 24.008108),
+ LatLng(53.1794024, 24.0166032),
+ LatLng(53.1845447, 24.0302469),
+ LatLng(53.1924883, 24.0363394),
+ LatLng(53.2057499, 24.0254405),
+ LatLng(53.2051506, 24.0132852),
+ LatLng(53.2024523, 24.0061201),
+ LatLng(53.1951594, 24.0030663),
+ )
+ ),
+ Polyline(
+ name = "skidel",
+ points = persistentListOf(
+ LatLng(53.5987627, 24.2287032),
+ LatLng(53.6009458, 24.2445713),
+ LatLng(53.6021933, 24.2643075),
+ LatLng(53.5940372, 24.2696849),
+ LatLng(53.5853207, 24.2788109),
+ LatLng(53.574621, 24.2673954),
+ LatLng(53.5645626, 24.2452236),
+ LatLng(53.5586105, 24.233416),
+ LatLng(53.5614393, 24.1987775),
+ LatLng(53.5639876, 24.189982),
+ LatLng(53.5768031, 24.1869358),
+ LatLng(53.5857289, 24.1879764),
+ LatLng(53.5988326, 24.228768),
+ LatLng(53.5987627, 24.2287032),
+ )
+ ),
+ Polyline(
+ name = "ozery",
+ points = persistentListOf(
+ LatLng(53.7325917, 24.1361288),
+ LatLng(53.7083211, 24.1629872),
+ LatLng(53.7031261, 24.1780764),
+ LatLng(53.7110637, 24.1976543),
+ LatLng(53.7219041, 24.2006492),
+ LatLng(53.7271092, 24.198684),
+ LatLng(53.7375152, 24.1789478),
+ LatLng(53.7325917, 24.1361288)
+ )
+ ),
+ Polyline(
+ name = "porechye",
+ points = persistentListOf(
+ LatLng(53.8724632, 24.1232477),
+ LatLng(53.8676351, 24.1401893),
+ LatLng(53.8736557, 24.1598826),
+ LatLng(53.8885988, 24.150621),
+ LatLng(53.8964367, 24.1341456),
+ LatLng(53.8826815, 24.1146668),
+ LatLng(53.8724632, 24.1232477)
+ )
+ ),
+ Polyline(
+ name = "volkovysk",
+ points = persistentListOf(
+ LatLng(53.1762187, 24.4098844),
+ LatLng(53.1501803, 24.3906566),
+ LatLng(53.1213524, 24.391343),
+ LatLng(53.1192238, 24.4249232),
+ LatLng(53.1277888, 24.465883),
+ LatLng(53.1369013, 24.4754937),
+ LatLng(53.1496657, 24.4985479),
+ LatLng(53.1689081, 24.4968317),
+ LatLng(53.1799215, 24.4569081),
+ LatLng(53.1762187, 24.4098844)
+ )
+ ),
+ )
+ )
+ )
+ }
+
+ fun onPointsChanged(name: String, points: PersistentList) {
+ _polylines.tryEmit(
+ when (val t = _polylines.value) {
+ is Loaded -> {
+ Loaded(
+ t.polyline.map { polyline ->
+ if (polyline.name == name) {
+ polyline.copy(points = points)
+ } else {
+ polyline
+ }
+ }.toPersistentList()
+ )
+ }
+ None -> t
+ }
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/settings/build.gradle.kts b/features/settings/build.gradle.kts
index 1299ac4d0..aea6719d9 100644
--- a/features/settings/build.gradle.kts
+++ b/features/settings/build.gradle.kts
@@ -6,6 +6,10 @@ plugins {
android {
namespace = "com.egoriku.grodnoroads.settings"
+
+ buildFeatures {
+ buildConfig = true
+ }
}
dependencies {
diff --git a/features/settings/src/main/kotlin/com/egoriku/grodnoroads/setting/screen/ui/SettingsUi.kt b/features/settings/src/main/kotlin/com/egoriku/grodnoroads/setting/screen/ui/SettingsUi.kt
index 680d73a5f..d4415a17b 100644
--- a/features/settings/src/main/kotlin/com/egoriku/grodnoroads/setting/screen/ui/SettingsUi.kt
+++ b/features/settings/src/main/kotlin/com/egoriku/grodnoroads/setting/screen/ui/SettingsUi.kt
@@ -19,6 +19,7 @@ import com.egoriku.grodnoroads.resources.R
import com.egoriku.grodnoroads.setting.screen.ui.section.PrivacyPolicySection
import com.egoriku.grodnoroads.setting.screen.ui.section.SocialNetworkSection
import com.egoriku.grodnoroads.setting.screen.ui.section.VersionSection
+import com.egoriku.grodnoroads.settings.BuildConfig
import com.egoriku.grodnoroads.shared.appcomponent.FeatureFlags.settingsNextFeaturesEnabled
import com.egoriku.grodnoroads.shared.appcomponent.Page
@@ -90,6 +91,15 @@ internal fun SettingsUi(
onSettingClick(Page.FAQ)
}
)
+ if (BuildConfig.DEBUG) {
+ SettingsItem(
+ icon = Icons.Filled.Adb,
+ text = stringResource(R.string.settings_section_debug_tools),
+ onClick = {
+ onSettingClick(Page.DebugTools)
+ }
+ )
+ }
Spacer(modifier = Modifier.weight(1f))
SocialNetworkSection()
diff --git a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/MapUpdater.kt b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/MapUpdater.kt
index 9ca21be21..97849cf1b 100644
--- a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/MapUpdater.kt
+++ b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/MapUpdater.kt
@@ -4,10 +4,8 @@ import androidx.compose.foundation.layout.LayoutScopeMarker
import androidx.compose.runtime.Immutable
import androidx.compose.ui.geometry.Offset
import com.egoriku.grodnoroads.maps.compose.decorator.MapPaddingDecorator
-import com.google.android.gms.maps.model.BitmapDescriptor
-import com.google.android.gms.maps.model.LatLng
-import com.google.android.gms.maps.model.Marker
-import com.google.android.gms.maps.model.MarkerOptions
+import com.google.android.gms.maps.model.*
+import com.google.maps.android.ktx.OnMarkerDragEvent
import kotlinx.coroutines.flow.SharedFlow
@LayoutScopeMarker
@@ -17,6 +15,7 @@ interface MapUpdater {
var lastLocation: LatLng?
val clickedMarker: SharedFlow
+ val draggedMarker: SharedFlow
fun addMarker(
position: LatLng,
@@ -28,6 +27,7 @@ interface MapUpdater {
): Marker?
fun addMarker(markerOptions: MarkerOptions): Marker?
+ fun addPolygon(polygonOptions: PolygonOptions): Polygon
fun zoomIn()
fun zoomOut()
diff --git a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Marker.kt b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Marker.kt
index 047f0553c..21197166d 100644
--- a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Marker.kt
+++ b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Marker.kt
@@ -6,6 +6,7 @@ import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -28,6 +29,36 @@ fun rememberSimpleMarker(
return marker
}
+context(MapUpdater)
+@Composable
+fun rememberDraggableMarker(
+ tag: String,
+ markerOptions: () -> MarkerOptions,
+ onPositionChange: (StableLatLng) -> Unit
+): Marker? {
+ var marker by remember { mutableStateOf(null) }
+
+ LaunchedEffect(marker) {
+ draggedMarker
+ .filterNotNull()
+ .filter { it.marker == marker }
+ .onEach {
+ onPositionChange(StableLatLng(it.marker.position))
+ }
+ .launchIn(this)
+ }
+
+ DisposableEffect(tag) {
+ marker = addMarker(markerOptions = markerOptions())
+
+ onDispose {
+ marker?.remove()
+ }
+ }
+
+ return marker
+}
+
context(MapUpdater)
@Composable
fun rememberIconMarker(
diff --git a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Polylgon.kt b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Polylgon.kt
new file mode 100644
index 000000000..e37824f9a
--- /dev/null
+++ b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Polylgon.kt
@@ -0,0 +1,24 @@
+package com.egoriku.grodnoroads.maps.compose
+
+import androidx.compose.runtime.*
+import com.google.android.gms.maps.model.Polygon
+import com.google.android.gms.maps.model.PolygonOptions
+
+context(MapUpdater)
+@Composable
+fun rememberPolygon(
+ tag: String,
+ polygonOptions: () -> PolygonOptions,
+): Polygon? {
+ var polygon by remember { mutableStateOf(null) }
+
+ DisposableEffect(tag) {
+ polygon = addPolygon(polygonOptions = polygonOptions())
+
+ onDispose {
+ polygon?.remove()
+ }
+ }
+
+ return polygon
+}
\ No newline at end of file
diff --git a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/impl/MapUpdaterImpl.kt b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/impl/MapUpdaterImpl.kt
index 06a8c56e6..d31489789 100644
--- a/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/impl/MapUpdaterImpl.kt
+++ b/libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/impl/MapUpdaterImpl.kt
@@ -13,18 +13,17 @@ import com.egoriku.grodnoroads.maps.core.extension.roundDistanceTo
import com.google.android.gms.maps.CameraUpdate
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
-import com.google.android.gms.maps.model.BitmapDescriptor
-import com.google.android.gms.maps.model.LatLng
-import com.google.android.gms.maps.model.Marker
-import com.google.android.gms.maps.model.MarkerOptions
+import com.google.android.gms.maps.model.*
+import com.google.maps.android.ktx.OnMarkerDragEvent
import com.google.maps.android.ktx.markerClickEvents
+import com.google.maps.android.ktx.markerDragEvents
import com.google.maps.android.ktx.model.cameraPosition
import com.google.maps.android.ktx.model.markerOptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -52,13 +51,20 @@ internal class MapUpdaterImpl(
get() = googleMap.cameraPosition.zoom
private val _clickedMarker = MutableSharedFlow(replay = 0)
- override val clickedMarker: SharedFlow = _clickedMarker
+ override val clickedMarker = _clickedMarker.asSharedFlow()
+
+ private val _draggedMarker = MutableSharedFlow(replay = 0)
+ override val draggedMarker = _draggedMarker.asSharedFlow()
override fun attach() {
logD("attach")
googleMap.markerClickEvents()
.onEach { _clickedMarker.emit(it) }
.launchIn(scope)
+
+ googleMap.markerDragEvents()
+ .onEach { _draggedMarker.emit(it) }
+ .launchIn(scope)
}
override fun setMaxZoomPreference(value: Float) {
@@ -91,6 +97,10 @@ internal class MapUpdaterImpl(
return googleMap.addMarker(markerOptions)
}
+ override fun addPolygon(polygonOptions: PolygonOptions): Polygon {
+ return googleMap.addPolygon(polygonOptions)
+ }
+
override fun detach() {
logD("detach")
googleMap.setOnMarkerClickListener(null)
diff --git a/libraries/resources/src/main/res/values-be-rBY/strings.xml b/libraries/resources/src/main/res/values-be-rBY/strings.xml
index 2df8e6410..b12aed32e 100644
--- a/libraries/resources/src/main/res/values-be-rBY/strings.xml
+++ b/libraries/resources/src/main/res/values-be-rBY/strings.xml
@@ -42,6 +42,7 @@
Што новага
Будучыя фічы
Часта задаюць пытанні
+ Інструменты адладкі
Асноўныя
diff --git a/libraries/resources/src/main/res/values-ru/strings.xml b/libraries/resources/src/main/res/values-ru/strings.xml
index ad33347e2..b16bf18c5 100644
--- a/libraries/resources/src/main/res/values-ru/strings.xml
+++ b/libraries/resources/src/main/res/values-ru/strings.xml
@@ -46,6 +46,7 @@
Что нового
Будущие фичи
Часто задаваемые вопросы
+ Инструменты отладки
Основные
diff --git a/libraries/resources/src/main/res/values/strings.xml b/libraries/resources/src/main/res/values/strings.xml
index 80c2ee2b1..abf42c205 100644
--- a/libraries/resources/src/main/res/values/strings.xml
+++ b/libraries/resources/src/main/res/values/strings.xml
@@ -48,6 +48,7 @@
What\'s new
Next features
FAQ
+ Debug Tools
Main
diff --git a/settings.gradle.kts b/settings.gradle.kts
index de15d1a62..4b2215b6e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -29,6 +29,7 @@ include(":features:settings")
include(":features:setting:alerts")
include(":features:setting:appearance")
+include(":features:setting:debugTools")
include(":features:setting:faq")
include(":features:setting:map")
include(":features:setting:whatsnew")
diff --git a/shared/appComponent/src/main/kotlin/com/egoriku/grodnoroads/shared/appcomponent/Page.kt b/shared/appComponent/src/main/kotlin/com/egoriku/grodnoroads/shared/appcomponent/Page.kt
index 3259a33b7..106755286 100644
--- a/shared/appComponent/src/main/kotlin/com/egoriku/grodnoroads/shared/appcomponent/Page.kt
+++ b/shared/appComponent/src/main/kotlin/com/egoriku/grodnoroads/shared/appcomponent/Page.kt
@@ -3,6 +3,7 @@ package com.egoriku.grodnoroads.shared.appcomponent
enum class Page {
Alerts,
Appearance,
+ DebugTools,
FAQ,
Map,
NextFeatures,