From 71676c3fafd89f2f47c0fcea961954288f011ff5 Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Fri, 21 Jul 2023 15:37:08 +0200 Subject: [PATCH] Static images improvements (#933) 1. On devices less than xhdpi request a 1x image from MapTiler (such devices are generally old, slower and with little memory so avoiding to get the 2x image only to have to shrink it later could help). 2. Coerce too big width/height combos within the API limits keeping the aspect ratio (this will allow requests on big horizontal displays to succeed). 3. Don't crash when given weird width/height combos (i.e. zero or negative). 4. Introduce interfaces to hide this whole logic and make it easier for forks to implement their own. Related to: - https://github.com/vector-im/element-meta/issues/1678 --- docs/maps.md | 17 +- .../features/location/api/StaticMapView.kt | 17 +- .../location/api/internal/MapTilerConfig.kt | 30 +++ .../internal/MapTilerStaticMapUrlBuilder.kt | 93 +++++++++ .../MapTilerTileServerStyleUriBuilder.kt | 39 ++++ .../features/location/api/internal/MapUrls.kt | 74 ------- .../api/internal/StaticMapUrlBuilder.kt | 36 ++++ .../api/internal/TileServerStyleUriBuilder.kt | 50 +++++ .../MapTilerStaticMapUrlBuilderTest.kt | 191 ++++++++++++++++++ .../MapTilerTileServerStyleUriBuilderTest.kt | 43 ++++ .../location/impl/show/ShowLocationView.kt | 1 - 11 files changed, 501 insertions(+), 90 deletions(-) create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt delete mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt create mode 100644 features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt create mode 100644 features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt create mode 100644 features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt diff --git a/docs/maps.md b/docs/maps.md index cc00905986..789d455f22 100644 --- a/docs/maps.md +++ b/docs/maps.md @@ -27,16 +27,21 @@ Place your API key in `local.properties` with the key services.maptiler.apikey=abCd3fGhijK1mN0pQr5t ``` +Optionally you can also place your custom MapTyler style ids for light and dark maps +in the `local.properties` with the keys `services.maptiler.lightMapId` and +`services.maptiler.darkMapId`. If you don't specify these, the default MapTiler "basic-v2" +styles will be used. + ## Making releasable builds with MapTiler To insert the MapTiler API key when building an APK, set the `ELEMENT_ANDROID_MAPTILER_API_KEY` environment variable in your build -environment. +environment. +If you've added custom styles also set the `ELEMENT_ANDROID_MAPTILER_LIGHT_MAP_ID` +and `ELEMENT_ANDROID_MAPTILER_DARK_MAP_ID` environment variables accordingly. ## Using other map sources or MapTiler styles -If you wish to use an alternative map provider, or custom MapTiler styles, -you can customise the functions in -`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt`. -We've kept this file small and self contained to minimise the chances of merge -collisions in forks. +If you wish to use an alternative map provider, you can provide your own implementations of +`TileServerStyleUriBuilder` and `StaticMapUrlBuilder` in +`features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/`. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt index 03aa21b4c8..39b3e8a9a1 100644 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/StaticMapView.kt @@ -29,16 +29,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import coil.compose.AsyncImagePainter import coil.compose.rememberAsyncImagePainter import coil.request.ImageRequest 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.staticMapUrl import io.element.android.libraries.designsystem.preview.DayNightPreviews import io.element.android.libraries.designsystem.preview.ElementPreview -import io.element.android.libraries.designsystem.text.toDp import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.theme.ElementTheme import timber.log.Timber @@ -65,23 +65,22 @@ fun StaticMapView( ) { val context = LocalContext.current var retryHash by remember { mutableStateOf(0) } + val builder = remember { StaticMapUrlBuilder(context) } val painter = rememberAsyncImagePainter( model = if (constraints.isZero) { // Avoid building a URL if any of the size constraints is zero (else it will thrown an exception). null } else { - ImageRequest.Builder(LocalContext.current) + ImageRequest.Builder(context) .data( - staticMapUrl( - context = context, + builder.build( lat = lat, lon = lon, zoom = zoom, darkMode = darkMode, - // Size the map based on DP rather than pixels, as otherwise the features and attribution - // end up being illegibly tiny on high density displays. - width = constraints.maxWidth.toDp().value.toInt(), - height = constraints.maxHeight.toDp().value.toInt(), + width = constraints.maxWidth, + height = constraints.maxHeight, + density = LocalDensity.current.density, ) ) .size(width = constraints.maxWidth, height = constraints.maxHeight) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt new file mode 100644 index 0000000000..9e20d5179b --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerConfig.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import android.content.Context +import io.element.android.features.location.api.R + +internal const val MAPTILER_BASE_URL = "https://api.maptiler.com/maps" + +internal fun Context.mapId(darkMode: Boolean) = when (darkMode) { + true -> getString(R.string.maptiler_dark_map_id) + false -> getString(R.string.maptiler_light_map_id) +} + +internal val Context.apiKey: String + get() = getString(R.string.maptiler_api_key) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt new file mode 100644 index 0000000000..72fcaeb06c --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilder.kt @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import android.content.Context +import kotlin.math.roundToInt + +/** + * Builds an URL for MapTiler's Static Maps API. + * + * https://docs.maptiler.com/cloud/api/static-maps/ + */ +internal class MapTilerStaticMapUrlBuilder( + private val apiKey: String, + private val lightMapId: String, + private val darkMapId: String, +) : StaticMapUrlBuilder { + + constructor(context: Context) : this( + apiKey = context.apiKey, + lightMapId = context.mapId(darkMode = false), + darkMapId = context.mapId(darkMode = true), + ) + + override fun build( + lat: Double, + lon: Double, + zoom: Double, + darkMode: Boolean, + width: Int, + height: Int, + density: Float + ): String { + val mapId = if (darkMode) darkMapId else lightMapId + val zoom = zoom.coerceIn(zoomRange) + + // Request @2x density for xhdpi and above (xhdpi == 320dpi == 2x density). + val is2x = density >= 2 + + // Scale requested width/height according to the reported display density. + val (width, height) = coerceWidthAndHeight( + width = (width / density).roundToInt(), + height = (height / density).roundToInt(), + is2x = is2x, + ) + + val scale = if (is2x) "@2x" else "" + + // Since Maptiler doesn't support arbitrary dpi scaling, we stick to 2x sized + // images even on displays with density higher than 2x, thereby yielding an + // image smaller than the available space in pixels. + // The resulting image will have to be scaled to fit the available space in order + // to keep the perceived content size constant at the expense of sharpness. + return "$MAPTILER_BASE_URL/${mapId}/static/${lon},${lat},${zoom}/${width}x${height}${scale}.webp?key=${apiKey}&attribution=bottomleft" + } +} + +private fun coerceWidthAndHeight(width: Int, height: Int, is2x: Boolean): Pair { + if (width <= 0 || height <= 0) { + // This effectively yields an URL which asks for a 0x0 image which will result in an HTTP error, + // but it's better than e.g. asking for a 1x1 image which would be unreadable and increase usage costs. + return 0 to 0 + } + val aspectRatio = width.toDouble() / height.toDouble() + val range = if (is2x) widthHeightRange2x else widthHeightRange + return if (width >= height) { + width.coerceIn(range).let { width -> + width to (width / aspectRatio).roundToInt() + } + } else { + height.coerceIn(range).let { height -> + (height * aspectRatio).roundToInt() to height + } + } +} + +private val widthHeightRange = 1..2048 // API will error if outside 1-2048 range @1x. +private val widthHeightRange2x = 1..1024 // API will error if outside 1-1024 range @2x. +private val zoomRange = 0.0..22.0 // API will error if outside 0-22 range. diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt new file mode 100644 index 0000000000..6972e45330 --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilder.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:JvmName("TileServerStyleUriBuilderKt") + +package io.element.android.features.location.api.internal + +import android.content.Context + +internal class MapTilerTileServerStyleUriBuilder( + private val apiKey: String, + private val lightMapId: String, + private val darkMapId: String, +) : TileServerStyleUriBuilder { + + constructor(context: Context) : this( + apiKey = context.apiKey, + lightMapId = context.mapId(darkMode = false), + darkMapId = context.mapId(darkMode = true), + ) + + override fun build(darkMode: Boolean): String { + val mapId = if (darkMode) darkMapId else lightMapId + return "${MAPTILER_BASE_URL}/${mapId}/style.json?key=${apiKey}" + } +} diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt deleted file mode 100644 index 2640896a78..0000000000 --- a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/MapUrls.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2023 New Vector Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.element.android.features.location.api.internal - -import android.content.Context -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalContext -import io.element.android.features.location.api.R -import io.element.android.libraries.theme.ElementTheme - -/** - * Provides the URL to an image that contains a statically-generated map of the given location. - */ -fun staticMapUrl( - context: Context, - lat: Double, - lon: Double, - zoom: Double, - width: Int, - height: Int, - darkMode: Boolean, -): String { - return "${context.baseUrl(darkMode)}/static/${lon},${lat},${zoom}/${width}x${height}@2x.webp?key=${context.apiKey}&attribution=bottomleft" -} - -/** - * Utility function to remember the tile server URL based on the current theme. - */ -@Composable -fun rememberTileStyleUrl(): String { - val context = LocalContext.current - val darkMode = !ElementTheme.isLightTheme - return remember(darkMode) { - tileStyleUrl( - context = context, - darkMode = darkMode - ) - } -} - -/** - * Provides the URL to a MapLibre style document, used for rendering dynamic maps. - */ -private fun tileStyleUrl( - context: Context, - darkMode: Boolean, -): String { - return "${context.baseUrl(darkMode)}/style.json?key=${context.apiKey}" -} - -private fun Context.baseUrl(darkMode: Boolean) = - "https://api.maptiler.com/maps/" + - if (darkMode) - getString(R.string.maptiler_dark_map_id) - else - getString(R.string.maptiler_light_map_id) - -private val Context.apiKey: String - get() = getString(R.string.maptiler_api_key) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt new file mode 100644 index 0000000000..8c29b5c13d --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/StaticMapUrlBuilder.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import android.content.Context + +/** + * Builds an URL for a 3rd party service provider static maps API. + */ +interface StaticMapUrlBuilder { + fun build( + lat: Double, + lon: Double, + zoom: Double, + darkMode: Boolean, + width: Int, + height: Int, + density: Float, + ): String +} + +fun StaticMapUrlBuilder(context: Context): StaticMapUrlBuilder = MapTilerStaticMapUrlBuilder(context = context) diff --git a/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt new file mode 100644 index 0000000000..ece32cbf5a --- /dev/null +++ b/features/location/api/src/main/kotlin/io/element/android/features/location/api/internal/TileServerStyleUriBuilder.kt @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import io.element.android.libraries.theme.ElementTheme + +/** + * Builds a style URI for a MapLibre compatible tile server. + * + * Used for rendering dynamic maps. + */ +interface TileServerStyleUriBuilder { + fun build( + darkMode: Boolean, + ): String +} + +fun TileServerStyleUriBuilder(context: Context): TileServerStyleUriBuilder = MapTilerTileServerStyleUriBuilder(context = context) + +/** + * Provides and remembers a style URI for a MapLibre compatible tile server. + * + * Used for rendering dynamic maps. + */ +@Composable +fun rememberTileStyleUrl(): String { + val context = LocalContext.current + val darkMode = !ElementTheme.isLightTheme + return remember(darkMode) { + TileServerStyleUriBuilder(context).build(darkMode) + } +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt new file mode 100644 index 0000000000..c359777ee7 --- /dev/null +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerStaticMapUrlBuilderTest.kt @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MapTilerStaticMapUrlBuilderTest { + + private val builder = MapTilerStaticMapUrlBuilder( + apiKey = "anApiKey", + lightMapId = "aLightMapId", + darkMapId = "aDarkMapId", + ) + + @Test + fun `static map 1x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 800, + height = 600, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 1,5x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 1200, + height = 900, + density = 1.5f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 2x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 1600, + height = 1200, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `static map 3x density`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2400, + height = 1800, + density = 3f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/800x600@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `too big image is coerced keeping aspect ratio`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 4096, + height = 2048, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/2048x1024.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2048, + height = 4096, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x2048.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 4096, + height = 2048, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x512@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 2048, + height = 4096, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/512x1024@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = Int.MAX_VALUE, + height = Int.MAX_VALUE, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/1024x1024@2x.webp?key=anApiKey&attribution=bottomleft") + } + + @Test + fun `too small image is coerced to 0x0`() { + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 0, + height = 0, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = 0, + height = 0, + density = 2f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0@2x.webp?key=anApiKey&attribution=bottomleft") + + assertThat( + builder.build( + lat = 1.23, + lon = -4.56, + zoom = 7.8, + darkMode = false, + width = Int.MIN_VALUE, + height = Int.MIN_VALUE, + density = 1f, + ) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/static/-4.56,1.23,7.8/0x0.webp?key=anApiKey&attribution=bottomleft") + } +} diff --git a/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt new file mode 100644 index 0000000000..abff83c582 --- /dev/null +++ b/features/location/api/src/test/kotlin/io/element/android/features/location/api/internal/MapTilerTileServerStyleUriBuilderTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.location.api.internal + +import com.google.common.truth.Truth.assertThat +import org.junit.Test + +class MapTilerTileServerStyleUriBuilderTest { + + private val builder = MapTilerTileServerStyleUriBuilder( + apiKey = "anApiKey", + lightMapId = "aLightMapId", + darkMapId = "aDarkMapId", + ) + + @Test + fun `light map uri`() { + assertThat( + builder.build(darkMode = false) + ).isEqualTo("https://api.maptiler.com/maps/aLightMapId/style.json?key=anApiKey") + } + + @Test + fun `dark map uri`() { + assertThat( + builder.build(darkMode = true) + ).isEqualTo("https://api.maptiler.com/maps/aDarkMapId/style.json?key=anApiKey") + } +} diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt index 7726bbf9cc..0e114a43b9 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/show/ShowLocationView.kt @@ -40,7 +40,6 @@ import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import io.element.android.features.location.api.internal.rememberTileStyleUrl import io.element.android.features.location.impl.MapDefaults -import io.element.android.features.location.impl.send.SendLocationState import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight