mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Move the custom tile server URL config to the Element wellknown content (#7416)
* Move the custom tile server URL config to the Element wellknown content * Iteration: use whole `MapTilerConfig` in the remote config
This commit is contained in:
committed by
GitHub
parent
ddbc6a2a33
commit
22195a47f2
@@ -70,6 +70,7 @@ dependencies {
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
implementation(projects.libraries.uiStrings)
|
||||
implementation(projects.libraries.wellknown.api)
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.datetime)
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.location.api
|
||||
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
|
||||
/**
|
||||
* Provides a [MapTilerConfig] for the MapLibre maps.
|
||||
*/
|
||||
val LocalMapTilerConfig = staticCompositionLocalOf {
|
||||
MapTilerConfig(
|
||||
apiKey = BuildConfig.MAPTILER_API_KEY,
|
||||
lightStyleId = BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||
darkStyleId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||
baseUrl = BuildConfig.MAPTILER_BASE_URL
|
||||
)
|
||||
}
|
||||
+7
-2
@@ -38,13 +38,14 @@ import coil3.request.ImageRequest
|
||||
import coil3.request.SuccessResult
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.features.location.api.internal.StaticMapPlaceholder
|
||||
import io.element.android.features.location.api.internal.StaticMapUrlBuilder
|
||||
import io.element.android.features.location.api.internal.centerBottomEdge
|
||||
import io.element.android.features.location.api.internal.rememberStaticMapBuilder
|
||||
import io.element.android.libraries.designsystem.components.LocationPin
|
||||
import io.element.android.libraries.designsystem.components.PinVariant
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
|
||||
/**
|
||||
* Shows a static map image downloaded via a third party service's static maps API.
|
||||
@@ -57,6 +58,7 @@ import io.element.android.libraries.designsystem.utils.CommonDrawables
|
||||
*/
|
||||
@Composable
|
||||
fun StaticMapView(
|
||||
mapTilerConfig: MapTilerConfig?,
|
||||
location: Location?,
|
||||
zoom: Double,
|
||||
pinVariant: PinVariant,
|
||||
@@ -94,6 +96,7 @@ fun StaticMapView(
|
||||
}
|
||||
// Cases 3 & 4: Non-null location - fetch map
|
||||
else -> LoadableMapContent(
|
||||
mapTilerConfig = mapTilerConfig,
|
||||
location = location,
|
||||
zoom = zoom,
|
||||
pinVariant = pinVariant,
|
||||
@@ -124,6 +127,7 @@ private fun BoxWithConstraintsScope.StaleMapContent(
|
||||
|
||||
@Composable
|
||||
private fun BoxWithConstraintsScope.LoadableMapContent(
|
||||
mapTilerConfig: MapTilerConfig?,
|
||||
location: Location,
|
||||
zoom: Double,
|
||||
pinVariant: PinVariant,
|
||||
@@ -132,7 +136,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var retryHash by remember { mutableIntStateOf(0) }
|
||||
val builder = remember { StaticMapUrlBuilder() }
|
||||
val builder = rememberStaticMapBuilder(mapTilerConfig)
|
||||
|
||||
val (painter, state, contentScale) = if (LocalInspectionMode.current) {
|
||||
val painter = painterResource(CommonDrawables.sample_map)
|
||||
@@ -203,6 +207,7 @@ private fun BoxWithConstraintsScope.LoadableMapContent(
|
||||
@Composable
|
||||
internal fun StaticMapViewPreview() = ElementPreview {
|
||||
StaticMapView(
|
||||
mapTilerConfig = null,
|
||||
location = Location(0.0, 0.0),
|
||||
zoom = 0.0,
|
||||
contentDescription = null,
|
||||
|
||||
+8
@@ -9,6 +9,7 @@
|
||||
package io.element.android.features.location.api.internal
|
||||
|
||||
import io.element.android.features.location.api.BuildConfig
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@@ -29,6 +30,13 @@ internal class MapTilerStaticMapUrlBuilder(
|
||||
darkMapId = BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||
)
|
||||
|
||||
constructor(mapTilerConfig: MapTilerConfig) : this(
|
||||
baseUrl = (mapTilerConfig.baseUrl ?: BuildConfig.MAPTILER_BASE_URL).removeSuffix("/"),
|
||||
apiKey = mapTilerConfig.apiKey.takeIf { it.isNotBlank() } ?: BuildConfig.MAPTILER_API_KEY,
|
||||
lightMapId = mapTilerConfig.lightStyleId ?: BuildConfig.MAPTILER_LIGHT_MAP_ID,
|
||||
darkMapId = mapTilerConfig.darkStyleId ?: BuildConfig.MAPTILER_DARK_MAP_ID,
|
||||
)
|
||||
|
||||
override fun build(
|
||||
lat: Double,
|
||||
lon: Double,
|
||||
|
||||
+9
-5
@@ -11,6 +11,7 @@
|
||||
package io.element.android.features.location.api.internal
|
||||
|
||||
import io.element.android.features.location.api.BuildConfig
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
|
||||
internal class MapTilerTileServerStyleUriBuilder(
|
||||
private val baseUrl: String,
|
||||
@@ -26,14 +27,17 @@ internal class MapTilerTileServerStyleUriBuilder(
|
||||
)
|
||||
|
||||
override fun build(
|
||||
customMapStyleUrl: String?,
|
||||
customMapTilerConfig: MapTilerConfig?,
|
||||
darkMode: Boolean,
|
||||
): String {
|
||||
return if (customMapStyleUrl.isNullOrBlank()) {
|
||||
val mapId = if (darkMode) darkMapId else lightMapId
|
||||
"$baseUrl/$mapId/style.json?key=$apiKey"
|
||||
val baseUrl = customMapTilerConfig?.baseUrl.takeIf { !it.isNullOrBlank() } ?: baseUrl
|
||||
val apiKey = customMapTilerConfig?.apiKey ?: apiKey
|
||||
val mapId = if (darkMode) {
|
||||
customMapTilerConfig?.darkStyleId ?: darkMapId
|
||||
} else {
|
||||
customMapStyleUrl
|
||||
customMapTilerConfig?.lightStyleId ?: lightMapId
|
||||
}
|
||||
|
||||
return "$baseUrl/$mapId/style.json?key=$apiKey"
|
||||
}
|
||||
}
|
||||
|
||||
+14
-1
@@ -8,6 +8,10 @@
|
||||
|
||||
package io.element.android.features.location.api.internal
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
|
||||
/**
|
||||
* Builds an URL for a 3rd party service provider static maps API.
|
||||
*/
|
||||
@@ -25,4 +29,13 @@ interface StaticMapUrlBuilder {
|
||||
fun isServiceAvailable(): Boolean
|
||||
}
|
||||
|
||||
fun StaticMapUrlBuilder(): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder()
|
||||
@Composable
|
||||
fun rememberStaticMapBuilder(mapTilerConfig: MapTilerConfig?): StaticMapUrlBuilder {
|
||||
return remember(mapTilerConfig) {
|
||||
if (mapTilerConfig != null) {
|
||||
MapTilerStaticMapUrlBuilder(mapTilerConfig)
|
||||
} else {
|
||||
MapTilerStaticMapUrlBuilder()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -11,6 +11,7 @@ package io.element.android.features.location.api.internal
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
|
||||
/**
|
||||
* Builds a style URI for a MapLibre compatible tile server.
|
||||
@@ -19,7 +20,7 @@ import io.element.android.compound.theme.ElementTheme
|
||||
*/
|
||||
interface TileServerStyleUriBuilder {
|
||||
fun build(
|
||||
customMapStyleUrl: String?,
|
||||
customMapTilerConfig: MapTilerConfig?,
|
||||
darkMode: Boolean,
|
||||
): String
|
||||
}
|
||||
@@ -31,12 +32,12 @@ interface TileServerStyleUriBuilder {
|
||||
*/
|
||||
@Composable
|
||||
fun rememberTileStyleUrl(
|
||||
customMapStyleUrl: String?,
|
||||
customMapTileConfig: MapTilerConfig?,
|
||||
): String {
|
||||
val darkMode = !ElementTheme.isLightTheme
|
||||
return remember(darkMode, customMapStyleUrl) {
|
||||
return remember(darkMode, customMapTileConfig) {
|
||||
MapTilerTileServerStyleUriBuilder().build(
|
||||
customMapStyleUrl = customMapStyleUrl,
|
||||
customMapTilerConfig = customMapTileConfig,
|
||||
darkMode = darkMode,
|
||||
)
|
||||
}
|
||||
|
||||
+24
-28
@@ -9,6 +9,7 @@
|
||||
package io.element.android.features.location.api.internal
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.wellknown.api.MapTilerConfig
|
||||
import org.junit.Test
|
||||
|
||||
class MapTilerTileServerStyleUriBuilderTest {
|
||||
@@ -23,7 +24,7 @@ class MapTilerTileServerStyleUriBuilderTest {
|
||||
fun `light map uri`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = null,
|
||||
customMapTilerConfig = null,
|
||||
darkMode = false,
|
||||
)
|
||||
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
||||
@@ -33,7 +34,7 @@ class MapTilerTileServerStyleUriBuilderTest {
|
||||
fun `dark map uri`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = null,
|
||||
customMapTilerConfig = null,
|
||||
darkMode = true,
|
||||
)
|
||||
).isEqualTo("https://base.url/aDarkMapId/style.json?key=anApiKey")
|
||||
@@ -43,49 +44,44 @@ class MapTilerTileServerStyleUriBuilderTest {
|
||||
fun `custom map uri light`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = "https://custom.url/style.json",
|
||||
customMapTilerConfig = MapTilerConfig(
|
||||
apiKey = "API_KEY",
|
||||
lightStyleId = "light",
|
||||
darkStyleId = "dark",
|
||||
baseUrl = "https://custom.url/style.json"
|
||||
),
|
||||
darkMode = false,
|
||||
)
|
||||
).isEqualTo("https://custom.url/style.json")
|
||||
).isEqualTo("https://custom.url/style.json/light/style.json?key=API_KEY")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom map uri dark`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = "https://custom.url/style.json",
|
||||
customMapTilerConfig = MapTilerConfig(
|
||||
apiKey = "API_KEY",
|
||||
lightStyleId = "light",
|
||||
darkStyleId = "dark",
|
||||
baseUrl = "https://custom.url/style.json"
|
||||
),
|
||||
darkMode = true,
|
||||
)
|
||||
).isEqualTo("https://custom.url/style.json")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom map uri with its own api key is not altered`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = "https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey",
|
||||
darkMode = false,
|
||||
)
|
||||
).isEqualTo("https://api.maptiler.com/maps/streets-v2/style.json?key=anOperatorKey")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `custom map uri with an existing query is not altered`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = "https://self.hosted/style.json?foo=bar",
|
||||
darkMode = true,
|
||||
)
|
||||
).isEqualTo("https://self.hosted/style.json?foo=bar")
|
||||
).isEqualTo("https://custom.url/style.json/dark/style.json?key=API_KEY")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `blank custom map uri falls back to the built-in style`() {
|
||||
assertThat(
|
||||
builder.build(
|
||||
customMapStyleUrl = " ",
|
||||
customMapTilerConfig = MapTilerConfig(
|
||||
apiKey = "API_KEY",
|
||||
lightStyleId = "light",
|
||||
darkStyleId = "dark",
|
||||
baseUrl = " "
|
||||
),
|
||||
darkMode = false,
|
||||
)
|
||||
).isEqualTo("https://base.url/aLightMapId/style.json?key=anApiKey")
|
||||
).isEqualTo("https://base.url/light/style.json?key=API_KEY")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user