From e465623c8a21dbb2856b40c0fab7a78db82cc6a6 Mon Sep 17 00:00:00 2001 From: Yahor Urbanovich Date: Sat, 28 Oct 2023 10:04:09 +0300 Subject: [PATCH] Add debug screen --- app/build.gradle.kts | 1 + .../screen/root/RoadsRootComponent.kt | 1 + .../screen/root/RoadsRootComponentImpl.kt | 9 +- .../grodnoroads/screen/root/RootContent.kt | 4 + features/setting/debugTools/.gitignore | 1 + features/setting/debugTools/build.gradle.kts | 26 +++ .../setting/debugTools/consumer-rules.pro | 0 .../debugTools/src/main/AndroidManifest.xml | 2 + .../setting/debugtools/DebugToolsScreen.kt | 173 ++++++++++++++++++ .../setting/debugtools/PolygonMarker.kt | 79 ++++++++ .../debugtools/data/PolylineRepository.kt | 144 +++++++++++++++ features/settings/build.gradle.kts | 4 + .../setting/screen/ui/SettingsUi.kt | 10 + .../grodnoroads/maps/compose/MapUpdater.kt | 8 +- .../grodnoroads/maps/compose/Marker.kt | 31 ++++ .../grodnoroads/maps/compose/Polylgon.kt | 24 +++ .../maps/compose/impl/MapUpdaterImpl.kt | 22 ++- .../src/main/res/values-be-rBY/strings.xml | 1 + .../src/main/res/values-ru/strings.xml | 1 + .../resources/src/main/res/values/strings.xml | 1 + settings.gradle.kts | 1 + .../grodnoroads/shared/appcomponent/Page.kt | 1 + 22 files changed, 530 insertions(+), 14 deletions(-) create mode 100644 features/setting/debugTools/.gitignore create mode 100644 features/setting/debugTools/build.gradle.kts create mode 100644 features/setting/debugTools/consumer-rules.pro create mode 100644 features/setting/debugTools/src/main/AndroidManifest.xml create mode 100644 features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/DebugToolsScreen.kt create mode 100644 features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/PolygonMarker.kt create mode 100644 features/setting/debugTools/src/main/kotlin/com/egoriku/grodnoroads/setting/debugtools/data/PolylineRepository.kt create mode 100644 libraries/maps/compose/src/main/kotlin/com/egoriku/grodnoroads/maps/compose/Polylgon.kt 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,