Use static map image in timeline.
This commit is contained in:
parent
4026ddb34f
commit
50279e3183
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.home.room.detail.timeline.factory
|
package im.vector.app.features.home.room.detail.timeline.factory
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
@ -127,7 +128,8 @@ class MessageItemFactory @Inject constructor(
|
||||||
private val session: Session,
|
private val session: Session,
|
||||||
private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker,
|
private val voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker,
|
||||||
private val locationPinProvider: LocationPinProvider,
|
private val locationPinProvider: LocationPinProvider,
|
||||||
private val vectorPreferences: VectorPreferences) {
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
private val resources: Resources) {
|
||||||
|
|
||||||
// TODO inject this properly?
|
// TODO inject this properly?
|
||||||
private var roomId: String = ""
|
private var roomId: String = ""
|
||||||
|
@ -207,11 +209,16 @@ class MessageItemFactory @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val width = resources.displayMetrics.widthPixels - dimensionConverter.dpToPx(60)
|
||||||
|
val height = dimensionConverter.dpToPx(200)
|
||||||
|
|
||||||
return MessageLocationItem_()
|
return MessageLocationItem_()
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
.locationData(locationData)
|
.locationData(locationData)
|
||||||
.userId(informationData.senderId)
|
.userId(informationData.senderId)
|
||||||
.locationPinProvider(locationPinProvider)
|
.locationPinProvider(locationPinProvider)
|
||||||
|
.mapWidth(width)
|
||||||
|
.mapHeight(height)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||||
.callback(mapCallback)
|
.callback(mapCallback)
|
||||||
|
|
|
@ -17,15 +17,17 @@
|
||||||
package im.vector.app.features.home.room.detail.timeline.item
|
package im.vector.app.features.home.room.detail.timeline.item
|
||||||
|
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import android.widget.ImageView
|
||||||
import com.airbnb.epoxy.EpoxyAttribute
|
import com.airbnb.epoxy.EpoxyAttribute
|
||||||
import com.airbnb.epoxy.EpoxyModelClass
|
import com.airbnb.epoxy.EpoxyModelClass
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.onClick
|
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.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.LocationData
|
||||||
import im.vector.app.features.location.MapState
|
import im.vector.app.features.location.getStaticMapUrl
|
||||||
import im.vector.app.features.location.MapTilerMapView
|
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||||
abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
|
abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
|
||||||
|
@ -46,6 +48,12 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var locationPinProvider: LocationPinProvider? = null
|
var locationPinProvider: LocationPinProvider? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var mapWidth: Int = 1200
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var mapHeight: Int = 800
|
||||||
|
|
||||||
override fun bind(holder: Holder) {
|
override fun bind(holder: Holder) {
|
||||||
super.bind(holder)
|
super.bind(holder)
|
||||||
renderSendState(holder.mapViewContainer, null)
|
renderSendState(holder.mapViewContainer, null)
|
||||||
|
@ -53,39 +61,28 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
||||||
val location = locationData ?: return
|
val location = locationData ?: return
|
||||||
val locationOwnerId = userId ?: return
|
val locationOwnerId = userId ?: return
|
||||||
|
|
||||||
holder.clickableMapArea.onClick {
|
holder.mapViewContainer.onClick {
|
||||||
callback?.onMapClicked()
|
callback?.onMapClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.mapView.initialize()
|
GlideApp.with(holder.staticMapImageView)
|
||||||
holder.mapView.render(
|
.load(getStaticMapUrl(location.latitude, location.longitude, INITIAL_MAP_ZOOM_IN_TIMELINE, mapWidth, mapHeight))
|
||||||
MapState(
|
.apply(RequestOptions.centerCropTransform())
|
||||||
zoomOnlyOnce = false,
|
.into(holder.staticMapImageView)
|
||||||
pinLocationData = location,
|
|
||||||
pinId = locationOwnerId,
|
|
||||||
pinDrawable = null
|
|
||||||
)
|
|
||||||
)
|
|
||||||
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
||||||
if (holder.view.isAttachedToWindow) {
|
GlideApp.with(holder.staticMapPinImageView)
|
||||||
holder.mapView.render(
|
.load(pinDrawable)
|
||||||
MapState(
|
.into(holder.staticMapPinImageView)
|
||||||
zoomOnlyOnce = false,
|
|
||||||
pinLocationData = location,
|
|
||||||
pinId = locationOwnerId,
|
|
||||||
pinDrawable = pinDrawable
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getViewType() = STUB_ID
|
override fun getViewType() = STUB_ID
|
||||||
|
|
||||||
class Holder : AbsMessageItem.Holder(STUB_ID) {
|
class Holder : AbsMessageItem.Holder(STUB_ID) {
|
||||||
val mapViewContainer by bind<ConstraintLayout>(R.id.mapViewContainer)
|
val mapViewContainer by bind<FrameLayout>(R.id.mapViewContainer)
|
||||||
val mapView by bind<MapTilerMapView>(R.id.mapView)
|
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
|
||||||
val clickableMapArea by bind<FrameLayout>(R.id.clickableMapArea)
|
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -16,6 +16,33 @@
|
||||||
|
|
||||||
package im.vector.app.features.location
|
package im.vector.app.features.location
|
||||||
|
|
||||||
const val INITIAL_MAP_ZOOM = 15.0
|
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 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_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds
|
||||||
const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ import javax.inject.Inject
|
||||||
/**
|
/**
|
||||||
* We should consider using SupportMapFragment for a out of the box lifecycle handling
|
* We should consider using SupportMapFragment for a out of the box lifecycle handling
|
||||||
*/
|
*/
|
||||||
class LocationSharingFragment @Inject constructor(
|
class LocationSharingFragment @Inject constructor() : VectorBaseFragment<FragmentLocationSharingBinding>() {
|
||||||
) : VectorBaseFragment<FragmentLocationSharingBinding>() {
|
|
||||||
|
|
||||||
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ import com.mapbox.mapboxsdk.maps.Style
|
||||||
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
|
import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
|
||||||
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
|
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
|
||||||
import com.mapbox.mapboxsdk.style.layers.Property
|
import com.mapbox.mapboxsdk.style.layers.Property
|
||||||
import im.vector.app.BuildConfig
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
@ -58,7 +57,7 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
getMapAsync { map ->
|
getMapAsync { map ->
|
||||||
map.setStyle(styleUrl) { style ->
|
map.setStyle(MAP_STYLE_URL) { style ->
|
||||||
mapRefs = MapRefs(
|
mapRefs = MapRefs(
|
||||||
map,
|
map,
|
||||||
SymbolManager(this, map, style),
|
SymbolManager(this, map, style),
|
||||||
|
@ -102,11 +101,7 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
Timber.d("## Location: zoomToLocation")
|
Timber.d("## Location: zoomToLocation")
|
||||||
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
|
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
|
||||||
.target(LatLng(latitude, longitude))
|
.target(LatLng(latitude, longitude))
|
||||||
.zoom(INITIAL_MAP_ZOOM)
|
.zoom(INITIAL_MAP_ZOOM_IN_PREVIEW)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,30 +6,25 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:cardCornerRadius="8dp">
|
app:cardCornerRadius="8dp">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<FrameLayout
|
||||||
android:id="@+id/mapViewContainer"
|
android:id="@+id/mapViewContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<im.vector.app.features.location.MapTilerMapView
|
<ImageView
|
||||||
android:id="@+id/mapView"
|
android:id="@+id/staticMapImageView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="200dp"
|
android:layout_height="200dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
android:contentDescription="@string/a11y_static_map_image" />
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:mapbox_renderTextureMode="true" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<ImageView
|
||||||
android:id="@+id/clickableMapArea"
|
android:id="@+id/staticMapPinImageView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="51dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="55dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/mapView"
|
android:layout_gravity="center"
|
||||||
app:layout_constraintEnd_toEndOf="@id/mapView"
|
android:layout_marginBottom="28dp"
|
||||||
app:layout_constraintStart_toStartOf="@id/mapView"
|
android:importantForAccessibility="no"
|
||||||
app:layout_constraintTop_toTopOf="@id/mapView" />
|
android:src="@drawable/bg_map_user_pin" />
|
||||||
|
</FrameLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
|
@ -3711,6 +3711,7 @@
|
||||||
<string name="location_activity_title_static_sharing">Share location</string>
|
<string name="location_activity_title_static_sharing">Share location</string>
|
||||||
<string name="location_activity_title_preview">Location</string>
|
<string name="location_activity_title_preview">Location</string>
|
||||||
<string name="a11y_location_share_icon">Share location</string>
|
<string name="a11y_location_share_icon">Share location</string>
|
||||||
|
<string name="a11y_static_map_image">Map</string>
|
||||||
<string name="location_share">Share location</string>
|
<string name="location_share">Share location</string>
|
||||||
<string name="template_location_not_available_dialog_title">${app_name} could not access your location</string>
|
<string name="template_location_not_available_dialog_title">${app_name} could not access your location</string>
|
||||||
<string name="template_location_not_available_dialog_content">${app_name} could not access your location. Please try again later.</string>
|
<string name="template_location_not_available_dialog_content">${app_name} could not access your location. Please try again later.</string>
|
||||||
|
|
Loading…
Reference in New Issue