Browse Source
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/1678pull/945/head
Marco Romano
1 year ago
committed by
GitHub
11 changed files with 501 additions and 90 deletions
@ -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) |
@ -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<Int, Int> { |
||||||
|
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. |
@ -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}" |
||||||
|
} |
||||||
|
} |
@ -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) |
|
@ -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) |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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") |
||||||
|
} |
||||||
|
} |
@ -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") |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue