Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored location sharing timeline items to use the mapserver configured by the wellknown file #7852

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/7852.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Refactored location sharing timeline items to use the mapserver configured by the wellknown file
2 changes: 0 additions & 2 deletions library/ui-strings/src/main/res/values/donottranslate.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,4 @@
<!-- onboarding english only word play -->
<string name="cut_the_slack_from_teams" translatable="false">Cut the slack from teams.</string>

<!-- WIP -->
<string name="location_map_view_copyright" translatable="false">© MapTiler © OpenStreetMap contributors</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import android.widget.TextView
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import com.bumptech.glide.request.RequestOptions
import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.VectorEpoxyHolder
Expand All @@ -36,6 +36,7 @@ import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.action.LocationUiData
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.zoomToLocation
import im.vector.app.features.media.ImageContentRenderer
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
import org.matrix.android.sdk.api.util.MatrixItem
Expand Down Expand Up @@ -98,10 +99,13 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
holder.body.isVisible = locationUiData == null
holder.mapViewContainer.isVisible = locationUiData != null
locationUiData?.let { safeLocationUiData ->
GlideApp.with(holder.staticMapImageView)
.load(safeLocationUiData.locationUrl)
.apply(RequestOptions.centerCropTransform())
.into(holder.staticMapImageView)
holder.staticMapView.getMapAsync { mapbox ->
mapbox.setStyle(safeLocationUiData.locationUrl)
safeLocationUiData.locationData?.let {
mapbox.zoomToLocation(it, animate = false)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I think the zoom level may not be correct. What we want in the timeline is INITIAL_MAP_ZOOM_IN_TIMELINE. And zoomToLocation is not using this value but another one. We may have to change the extension method to pass the required zoom level.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
mapbox.uiSettings.setAllGesturesEnabled(false)
}

safeLocationUiData.locationPinProvider.create(safeLocationUiData.locationOwnerId) { pinDrawable ->
GlideApp.with(holder.staticMapPinImageView)
Expand All @@ -124,7 +128,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
val timestamp by bind<TextView>(R.id.bottom_sheet_message_preview_timestamp)
val imagePreview by bind<ImageView>(R.id.bottom_sheet_message_preview_image)
val mapViewContainer by bind<FrameLayout>(R.id.mapViewContainer)
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
val staticMapView by bind<MapView>(R.id.staticMapView)
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.VectorFeatures
import im.vector.app.features.location.UrlMapProvider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import should be removed.

import im.vector.app.features.settings.VectorPreferences

class AttachmentTypeSelectorViewModel @AssistedInject constructor(
@Assisted initialState: AttachmentTypeSelectorViewState,
private val vectorFeatures: VectorFeatures,
private val vectorPreferences: VectorPreferences,
private val urlMapProvider: UrlMapProvider
artkoenig marked this conversation as resolved.
Show resolved Hide resolved
) : VectorViewModel<AttachmentTypeSelectorViewState, AttachmentTypeSelectorAction, EmptyViewEvents>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<AttachmentTypeSelectorViewModel, AttachmentTypeSelectorViewState> {
Expand All @@ -48,7 +50,7 @@ class AttachmentTypeSelectorViewModel @AssistedInject constructor(
init {
setState {
copy(
isLocationVisible = vectorFeatures.isLocationSharingEnabled(),
isLocationVisible = vectorFeatures.isLocationSharingEnabled() && urlMapProvider.isMapConfigurationAvailable(),
isVoiceBroadcastVisible = vectorFeatures.isVoiceBroadcastEnabled(),
isTextFormattingEnabled = vectorPreferences.isTextFormattingEnabled(),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import im.vector.app.features.analytics.extensions.toAnalyticsType
import im.vector.app.features.analytics.plan.Signup
import im.vector.app.features.analytics.store.AnalyticsStore
import im.vector.app.features.home.room.list.home.release.ReleaseNotesPreferencesStore
import im.vector.app.features.location.UrlMapProvider
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.onboarding.AuthenticationDescription
import im.vector.app.features.raw.wellknown.ElementWellKnown
Expand Down Expand Up @@ -95,6 +96,7 @@ class HomeActivityViewModel @AssistedInject constructor(
private val registerUnifiedPushUseCase: RegisterUnifiedPushUseCase,
private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
private val ensureFcmTokenIsRetrievedUseCase: EnsureFcmTokenIsRetrievedUseCase,
private val mapUrlProvider: UrlMapProvider,
artkoenig marked this conversation as resolved.
Show resolved Hide resolved
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {

@AssistedFactory
Expand Down Expand Up @@ -383,6 +385,9 @@ class HomeActivityViewModel @AssistedInject constructor(
}

val elementWellKnown = rawService.getElementWellknown(session.sessionParams)

mapUrlProvider.initWithWellknown(elementWellKnown)
artkoenig marked this conversation as resolved.
Show resolved Hide resolved

val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: false

// In case of account creation, it is already done before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActi
import im.vector.app.features.home.room.detail.upgrade.MigrateRoomBottomSheet
import im.vector.app.features.html.PillImageSpan
import im.vector.app.features.location.LocationSharingMode
import im.vector.app.features.location.UrlMapProvider
import im.vector.app.features.poll.PollMode
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.share.SharedData
Expand Down Expand Up @@ -121,6 +122,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
@Inject lateinit var buildMeta: BuildMeta
@Inject lateinit var session: Session
@Inject lateinit var errorTracker: ErrorTracker
@Inject lateinit var urlMapProvider: UrlMapProvider

private val roomId: String get() = withState(timelineViewModel) { it.roomId }

Expand Down Expand Up @@ -356,7 +358,7 @@ class MessageComposerFragment : VectorBaseFragment<FragmentComposerBinding>(), A
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@MessageComposerFragment)
attachmentTypeSelector.setAttachmentVisibility(
AttachmentType.LOCATION,
vectorFeatures.isLocationSharingEnabled(),
vectorFeatures.isLocationSharingEnabled() && urlMapProvider.isMapConfigurationAvailable(),
artkoenig marked this conversation as resolved.
Show resolved Hide resolved
)
attachmentTypeSelector.setAttachmentVisibility(
AttachmentType.POLL, !isThreadTimeLine()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
package im.vector.app.features.home.room.detail.timeline.action

import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.location.LocationData

/**
* Data used to display Location data in the message bottom sheet.
*/
data class LocationUiData(
val locationData: LocationData?,
val locationUrl: String,
val locationOwnerId: String?,
val locationPinProvider: LocationPinProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ 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.UrlMapProvider
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.ImageContentRenderer
Expand Down Expand Up @@ -228,13 +227,12 @@ class MessageActionsEpoxyController @Inject constructor(

val locationContent = state.timelineEvent()?.root?.getClearContent().toModel<MessageLocationContent>(catchError = true)
?: return null
val locationUrl = locationContent.toLocationData()
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
?: return null

val locationOwnerId = if (locationContent.isSelfLocation()) state.informationData.matrixItem.id else null

return LocationUiData(
locationUrl = locationUrl,
locationData = locationContent.toLocationData(),
locationUrl = urlMapProvider.getMapStyleUrl(),
locationOwnerId = locationOwnerId,
locationPinProvider = locationPinProvider,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocation
import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem_
import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
import im.vector.app.features.location.UrlMapProvider
import im.vector.app.features.location.toLocationData
import org.matrix.android.sdk.api.session.Session
Expand Down Expand Up @@ -105,13 +104,12 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
val width = timelineMediaSizeProvider.getMaxSize().first
val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)

val locationUrl = runningState.lastGeoUri.toLocationData()?.let {
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
}
val lastLocationData = runningState.lastGeoUri.toLocationData()

return MessageLiveLocationItem_()
.attributes(attributes)
.locationUrl(locationUrl)
.locationData(lastLocationData)
.mapStyleUrl(urlMapProvider.getMapStyleUrl())
.mapWidth(width)
.mapHeight(height)
.locationUserId(attributes.informationData.senderId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ 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.UrlMapProvider
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.ImageContentRenderer
Expand Down Expand Up @@ -222,15 +221,12 @@ class MessageItemFactory @Inject constructor(
val width = timelineMediaSizeProvider.getMaxSize().first
val height = dimensionConverter.dpToPx(MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)

val locationUrl = locationContent.toLocationData()?.let {
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
}

val locationUserId = if (locationContent.isSelfLocation()) informationData.senderId else null

return MessageLocationItem_()
.attributes(attributes)
.locationUrl(locationUrl)
.locationData(locationContent.toLocationData())
.mapStyleUrl(urlMapProvider.getMapStyleUrl())
.mapWidth(width)
.mapHeight(height)
.locationUserId(locationUserId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,34 @@

package im.vector.app.features.home.room.detail.timeline.item

import android.graphics.drawable.Drawable
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.mapbox.mapboxsdk.maps.MapView
import im.vector.app.R
import im.vector.app.core.glide.GlideApp
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
import im.vector.app.features.location.LocationData
import im.vector.app.features.location.MapLoadingErrorView
import im.vector.app.features.location.MapLoadingErrorViewState
import im.vector.app.features.location.zoomToLocation

abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
@LayoutRes layoutId: Int = R.layout.item_timeline_event_base
) : AbsMessageItem<H>(layoutId) {

@EpoxyAttribute
var locationUrl: String? = null
var locationData: LocationData? = null

@EpoxyAttribute
var mapStyleUrl: String? = null

@EpoxyAttribute
var locationUserId: String? = null
Expand All @@ -65,61 +64,52 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
}

private fun bindMap(holder: Holder) {
val location = locationUrl ?: return
val messageLayout = attributes.informationData.messageLayout
val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
messageLayout.cornersRadius.granularRoundedCorners()
} else {
val dimensionConverter = DimensionConverter(holder.view.resources)
RoundedCorners(dimensionConverter.dpToPx(8))
}
holder.staticMapImageView.updateLayoutParams {
holder.staticMapView.updateLayoutParams {
width = mapWidth
height = mapHeight
}
GlideApp.with(holder.staticMapImageView)
.load(location)
.apply(RequestOptions.centerCropTransform())
.placeholder(holder.staticMapImageView.drawable)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
holder.staticMapPinImageView.setImageDrawable(null)
holder.staticMapLoadingErrorView.isVisible = true
val mapErrorViewState = MapLoadingErrorViewState(imageCornerTransformation)
holder.staticMapLoadingErrorView.render(mapErrorViewState)
holder.staticMapCopyrightTextView.isVisible = false
return false
}

override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
locationPinProvider?.create(locationUserId) { pinDrawable ->
// we are not using Glide since it does not display it correctly when there is no user photo
holder.staticMapPinImageView.setImageDrawable(pinDrawable)
}
holder.staticMapLoadingErrorView.isVisible = false
holder.staticMapCopyrightTextView.isVisible = true
return false
}
})
.transform(imageCornerTransformation)
.into(holder.staticMapImageView)
holder.staticMapView.addOnDidFailLoadingMapListener {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some clean up about the map should be added in the unbind(holder: H) method. All listeners that can be removed should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

holder.staticMapLoadingErrorView.isVisible = true
val mapErrorViewState = MapLoadingErrorViewState(imageCornerTransformation)
holder.staticMapLoadingErrorView.render(mapErrorViewState)
}

holder.staticMapView.addOnDidFinishLoadingMapListener {
locationPinProvider?.create(locationUserId) { pinDrawable ->
holder.staticMapPinImageView.setImageDrawable(pinDrawable)
}
holder.staticMapLoadingErrorView.isVisible = false
}

holder.staticMapView.clipToOutline = true
holder.staticMapView.getMapAsync { mapbox ->
mapbox.setStyle(mapStyleUrl)
locationData?.let {
mapbox.zoomToLocation(it, animate = false)
}
mapbox.uiSettings.setAllGesturesEnabled(false)
mapbox.addOnMapClickListener {
attributes.itemClickListener?.invoke(holder.staticMapView)
true
}
mapbox.addOnMapLongClickListener {
attributes.itemLongClickListener?.onLongClick(holder.staticMapView)
true
}
}
}

abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
val staticMapView by bind<MapView>(R.id.staticMapView)
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
val staticMapLoadingErrorView by bind<MapLoadingErrorView>(R.id.staticMapLoadingError)
val staticMapCopyrightTextView by bind<TextView>(R.id.staticMapCopyrightTextView)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class LocationSharingFragment :

lifecycleScope.launchWhenCreated {
views.mapView.initialize(
url = urlMapProvider.getMapUrl(),
url = urlMapProvider.getMapStyleUrl(),
locationTargetChangeListener = this@LocationSharingFragment
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,24 @@ import com.mapbox.mapboxsdk.geometry.LatLng
import com.mapbox.mapboxsdk.geometry.LatLngBounds
import com.mapbox.mapboxsdk.maps.MapboxMap

fun MapboxMap?.zoomToLocation(locationData: LocationData, preserveCurrentZoomLevel: Boolean = false) {
fun MapboxMap?.zoomToLocation(locationData: LocationData, preserveCurrentZoomLevel: Boolean = false, animate: Boolean = true) {
val zoomLevel = if (preserveCurrentZoomLevel && this?.cameraPosition != null) {
cameraPosition.zoom
} else {
INITIAL_MAP_ZOOM_IN_PREVIEW
}
this?.easeCamera {
CameraPosition.Builder()
.target(LatLng(locationData.latitude, locationData.longitude))
.zoom(zoomLevel)
.build()
val cameraPosition = CameraPosition.Builder()
.target(LatLng(locationData.latitude, locationData.longitude))
.zoom(zoomLevel)
.build()
if(animate) {
this?.easeCamera {
cameraPosition
}
} else {
this?.moveCamera {
cameraPosition
}
}
}

Expand Down
Loading