Create a UrlMapProvider for a better handling of RTL languages, and build the URLs in the controllers
This commit is contained in:
parent
eff6942f82
commit
2ce3894562
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<bool name="is_rtl">true</bool>
|
||||
|
||||
</resources>
|
|
@ -4,4 +4,6 @@
|
|||
<!-- Created to detect what has to be implemented (especially in the settings) -->
|
||||
<bool name="false_not_implemented">false</bool>
|
||||
|
||||
<bool name="is_rtl">false</bool>
|
||||
|
||||
</resources>
|
|
@ -36,9 +36,6 @@ import im.vector.app.features.home.AvatarRenderer
|
|||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
|
||||
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
|
||||
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
|
||||
import im.vector.app.features.location.LocationData
|
||||
import im.vector.app.features.location.getStaticMapUrl
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
|
||||
import org.matrix.android.sdk.api.util.MatrixItem
|
||||
|
@ -74,7 +71,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
|||
var time: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var locationData: LocationData? = null
|
||||
var locationUrl: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var locationPinProvider: LocationPinProvider? = null
|
||||
|
@ -85,12 +82,6 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
|||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var userClicked: ClickListener? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var mapWidth: Int = 1200
|
||||
|
||||
@EpoxyAttribute
|
||||
var mapHeight: Int = 800
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
avatarRenderer.render(matrixItem, holder.avatar)
|
||||
|
@ -107,14 +98,14 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
|||
body.charSequence.findPillsAndProcess(coroutineScope) { it.bind(holder.body) }
|
||||
holder.timestamp.setTextOrHide(time)
|
||||
|
||||
if (locationData == null) {
|
||||
if (locationUrl == null) {
|
||||
holder.body.isVisible = true
|
||||
holder.mapViewContainer.isVisible = false
|
||||
} else {
|
||||
holder.body.isVisible = false
|
||||
holder.mapViewContainer.isVisible = true
|
||||
GlideApp.with(holder.staticMapImageView)
|
||||
.load(getStaticMapUrl(locationData!!.latitude, locationData!!.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight))
|
||||
.load(locationUrl)
|
||||
.apply(RequestOptions.centerCropTransform())
|
||||
.into(holder.staticMapImageView)
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@ import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
|
|||
import im.vector.app.features.home.room.detail.timeline.tools.createLinkMovementMethod
|
||||
import im.vector.app.features.home.room.detail.timeline.tools.linkify
|
||||
import im.vector.app.features.html.SpanUtils
|
||||
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
|
||||
import im.vector.app.features.location.LocationData
|
||||
import im.vector.app.features.location.UrlMapProvider
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
|
@ -62,6 +64,7 @@ class MessageActionsEpoxyController @Inject constructor(
|
|||
private val spanUtils: SpanUtils,
|
||||
private val eventDetailsFormatter: EventDetailsFormatter,
|
||||
private val dateFormatter: VectorDateFormatter,
|
||||
private val urlMapProvider: UrlMapProvider,
|
||||
private val locationPinProvider: LocationPinProvider
|
||||
) : TypedEpoxyController<MessageActionState>() {
|
||||
|
||||
|
@ -74,9 +77,11 @@ class MessageActionsEpoxyController @Inject constructor(
|
|||
val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL)
|
||||
val body = state.messageBody.linkify(host.listener)
|
||||
val bindingOptions = spanUtils.getBindingOptions(body)
|
||||
val locationData = state.timelineEvent()?.root?.getClearContent()?.toModel<MessageLocationContent>(catchError = true)?.let {
|
||||
LocationData.create(it.getUri())
|
||||
}
|
||||
val locationUrl = state.timelineEvent()?.root?.getClearContent()
|
||||
?.toModel<MessageLocationContent>(catchError = true)
|
||||
?.let { LocationData.create(it.getUri()) }
|
||||
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
|
||||
|
||||
bottomSheetMessagePreviewItem {
|
||||
id("preview")
|
||||
avatarRenderer(host.avatarRenderer)
|
||||
|
@ -89,7 +94,7 @@ class MessageActionsEpoxyController @Inject constructor(
|
|||
body(body.toEpoxyCharSequence())
|
||||
bodyDetails(host.eventDetailsFormatter.format(state.timelineEvent()?.root)?.toEpoxyCharSequence())
|
||||
time(formattedDate)
|
||||
locationData(locationData)
|
||||
locationUrl(locationUrl)
|
||||
locationPinProvider(host.locationPinProvider)
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,9 @@ import im.vector.app.features.html.EventHtmlRenderer
|
|||
import im.vector.app.features.html.PillsPostProcessor
|
||||
import im.vector.app.features.html.SpanUtils
|
||||
import im.vector.app.features.html.VectorHtmlCompressor
|
||||
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
|
||||
import im.vector.app.features.location.LocationData
|
||||
import im.vector.app.features.location.UrlMapProvider
|
||||
import im.vector.app.features.media.ImageContentRenderer
|
||||
import im.vector.app.features.media.VideoContentRenderer
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
|
@ -129,7 +131,9 @@ class MessageItemFactory @Inject constructor(
|
|||
private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker,
|
||||
private val locationPinProvider: LocationPinProvider,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val resources: Resources) {
|
||||
private val urlMapProvider: UrlMapProvider,
|
||||
private val resources: Resources
|
||||
) {
|
||||
|
||||
// TODO inject this properly?
|
||||
private var roomId: String = ""
|
||||
|
@ -212,13 +216,15 @@ class MessageItemFactory @Inject constructor(
|
|||
val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60)
|
||||
val height = dimensionConverter.dpToPx(200)
|
||||
|
||||
val locationUrl = locationData?.let {
|
||||
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
|
||||
}
|
||||
|
||||
return MessageLocationItem_()
|
||||
.attributes(attributes)
|
||||
.locationData(locationData)
|
||||
.locationUrl(locationUrl)
|
||||
.userId(informationData.senderId)
|
||||
.locationPinProvider(locationPinProvider)
|
||||
.mapWidth(width)
|
||||
.mapHeight(height)
|
||||
.highlighted(highlight)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.callback(mapCallback)
|
||||
|
|
|
@ -24,9 +24,6 @@ import im.vector.app.R
|
|||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.core.glide.GlideApp
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
|
||||
import im.vector.app.features.location.LocationData
|
||||
import im.vector.app.features.location.getStaticMapUrl
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
|
||||
|
@ -39,7 +36,7 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
|||
var callback: Callback? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var locationData: LocationData? = null
|
||||
var locationUrl: String? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var userId: String? = null
|
||||
|
@ -47,17 +44,11 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
|||
@EpoxyAttribute
|
||||
var locationPinProvider: LocationPinProvider? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
var mapWidth: Int = 1200
|
||||
|
||||
@EpoxyAttribute
|
||||
var mapHeight: Int = 800
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
renderSendState(holder.view, null)
|
||||
|
||||
val location = locationData ?: return
|
||||
val location = locationUrl ?: return
|
||||
val locationOwnerId = userId ?: return
|
||||
|
||||
holder.view.onClick {
|
||||
|
@ -65,7 +56,7 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
|||
}
|
||||
|
||||
GlideApp.with(holder.staticMapImageView)
|
||||
.load(getStaticMapUrl(location.latitude, location.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight))
|
||||
.load(location)
|
||||
.apply(RequestOptions.centerCropTransform())
|
||||
.into(holder.staticMapImageView)
|
||||
|
||||
|
|
|
@ -16,34 +16,10 @@
|
|||
|
||||
package im.vector.app.features.location
|
||||
|
||||
import im.vector.app.BuildConfig
|
||||
|
||||
const val MAP_STYLE_URL = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}"
|
||||
private const val STATIC_MAP_IMAGE_URL = "https://api.maptiler.com/maps/basic/static/"
|
||||
const val MAP_BASE_URL = "https://api.maptiler.com/maps/streets/style.json"
|
||||
const val STATIC_MAP_BASE_URL = "https://api.maptiler.com/maps/basic/static/"
|
||||
|
||||
const val INITIAL_MAP_ZOOM_IN_PREVIEW = 15.0
|
||||
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0
|
||||
const val MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds
|
||||
const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f
|
||||
|
||||
fun getStaticMapUrl(latitude: Double,
|
||||
longitude: Double,
|
||||
zoom: Double,
|
||||
width: Int,
|
||||
height: Int): String {
|
||||
return buildString {
|
||||
append(STATIC_MAP_IMAGE_URL)
|
||||
append(longitude)
|
||||
append(",")
|
||||
append(latitude)
|
||||
append(",")
|
||||
append(zoom)
|
||||
append("/")
|
||||
append(width)
|
||||
append("x")
|
||||
append(height)
|
||||
append(".png?key=")
|
||||
append(BuildConfig.mapTilerKey)
|
||||
append("&attribution=bottomleft")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import javax.inject.Inject
|
|||
* TODO Move locationPinProvider to a ViewModel
|
||||
*/
|
||||
class LocationPreviewFragment @Inject constructor(
|
||||
private val urlMapProvider: UrlMapProvider,
|
||||
private val locationPinProvider: LocationPinProvider
|
||||
) : VectorBaseFragment<FragmentLocationPreviewBinding>() {
|
||||
|
||||
|
@ -53,7 +54,7 @@ class LocationPreviewFragment @Inject constructor(
|
|||
|
||||
mapView = WeakReference(views.mapView)
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
views.mapView.initialize()
|
||||
views.mapView.initialize(urlMapProvider.mapUrl)
|
||||
loadPinDrawable()
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,9 @@ import javax.inject.Inject
|
|||
/**
|
||||
* We should consider using SupportMapFragment for a out of the box lifecycle handling
|
||||
*/
|
||||
class LocationSharingFragment @Inject constructor() : VectorBaseFragment<FragmentLocationSharingBinding>() {
|
||||
class LocationSharingFragment @Inject constructor(
|
||||
private val urlMapProvider: UrlMapProvider
|
||||
) : VectorBaseFragment<FragmentLocationSharingBinding>() {
|
||||
|
||||
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
||||
|
||||
|
@ -51,7 +53,7 @@ class LocationSharingFragment @Inject constructor() : VectorBaseFragment<Fragmen
|
|||
|
||||
mapView = WeakReference(views.mapView)
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
views.mapView.initialize()
|
||||
views.mapView.initialize(urlMapProvider.mapUrl)
|
||||
|
||||
views.shareLocationContainer.debouncedClicks {
|
||||
viewModel.handle(LocationSharingAction.OnShareLocation)
|
||||
|
|
|
@ -27,7 +27,6 @@ import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
|
|||
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
|
||||
import com.mapbox.mapboxsdk.style.layers.Property
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class MapTilerMapView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -43,21 +42,16 @@ class MapTilerMapView @JvmOverloads constructor(
|
|||
val style: Style
|
||||
)
|
||||
|
||||
private var isInitializing = AtomicBoolean(false)
|
||||
private var mapRefs: MapRefs? = null
|
||||
private var initZoomDone = false
|
||||
|
||||
/**
|
||||
* For location fragments
|
||||
*/
|
||||
fun initialize() {
|
||||
Timber.d("## Location: initialize $isInitializing")
|
||||
if (isInitializing.getAndSet(true)) {
|
||||
return
|
||||
}
|
||||
|
||||
fun initialize(url: String) {
|
||||
Timber.d("## Location: initialize")
|
||||
getMapAsync { map ->
|
||||
map.setStyle(MAP_STYLE_URL) { style ->
|
||||
map.setStyle(url) { style ->
|
||||
mapRefs = MapRefs(
|
||||
map,
|
||||
SymbolManager(this, map, style),
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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 im.vector.app.features.location
|
||||
|
||||
import android.content.res.Resources
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.R
|
||||
import javax.inject.Inject
|
||||
|
||||
class UrlMapProvider @Inject constructor(
|
||||
private val resources: Resources
|
||||
) {
|
||||
private val keyParam = "?key=${BuildConfig.mapTilerKey}"
|
||||
|
||||
// This is static so no need for a fun
|
||||
val mapUrl = buildString {
|
||||
append(MAP_BASE_URL)
|
||||
append(keyParam)
|
||||
}
|
||||
|
||||
fun buildStaticMapUrl(locationData: LocationData,
|
||||
zoom: Double,
|
||||
width: Int,
|
||||
height: Int): String {
|
||||
return buildString {
|
||||
append(STATIC_MAP_BASE_URL)
|
||||
append(locationData.longitude)
|
||||
append(",")
|
||||
append(locationData.latitude)
|
||||
append(",")
|
||||
append(zoom)
|
||||
append("/")
|
||||
append(width)
|
||||
append("x")
|
||||
append(height)
|
||||
append(".png")
|
||||
append(keyParam)
|
||||
if (!resources.getBoolean(R.bool.is_rtl)) {
|
||||
// On LTR languages we want the legal mentions to be displayed on the bottom left of the image
|
||||
append("&attribution=bottomleft")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue