Support generic location pin.

This commit is contained in:
Onuray Sahin 2022-02-02 14:25:54 +03:00
parent 6213cc73c0
commit 37d35c9a7f
16 changed files with 68 additions and 17 deletions

View File

@ -64,4 +64,12 @@ data class MessageLocationContent(
) : MessageContent {
fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri
/**
* @return true if location asset is different than LocationAssetType.SELF
*/
fun isGenericLocation(): Boolean {
// Should behave like m.self if locationAsset is null
return locationAsset != null && locationAsset.type != LocationAssetType.SELF
}
}

View File

@ -76,6 +76,9 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
@EpoxyAttribute
var locationPinProvider: LocationPinProvider? = null
@EpoxyAttribute
var locationOwnerId: String? = null
@EpoxyAttribute
var movementMethod: MovementMethod? = null
@ -109,7 +112,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
.apply(RequestOptions.centerCropTransform())
.into(holder.staticMapImageView)
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
GlideApp.with(holder.staticMapPinImageView)
.load(pinDrawable)
.into(holder.staticMapPinImageView)

View File

@ -613,13 +613,14 @@ class RoomDetailFragment @Inject constructor(
}
private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) {
val isGenericLocation = locationContent.isGenericLocation()
navigator
.openLocationSharing(
context = requireContext(),
roomId = roomDetailArgs.roomId,
mode = LocationSharingMode.PREVIEW,
initialLocationData = locationContent.toLocationData(),
locationOwnerId = senderId
locationOwnerId = if (isGenericLocation) null else senderId
)
}

View File

@ -77,10 +77,12 @@ 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 locationUrl = state.timelineEvent()?.root?.getClearContent()
val locationContent = state.timelineEvent()?.root?.getClearContent()
?.toModel<MessageLocationContent>(catchError = true)
?.toLocationData()
val locationUrl = locationContent?.toLocationData()
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
val locationOwnerId = if (locationContent?.isGenericLocation().orFalse()) null else state.informationData.matrixItem.id
bottomSheetMessagePreviewItem {
id("preview")
@ -96,6 +98,7 @@ class MessageActionsEpoxyController @Inject constructor(
time(formattedDate)
locationUrl(locationUrl)
locationPinProvider(host.locationPinProvider)
locationOwnerId(locationOwnerId)
}
// Send state

View File

@ -207,10 +207,12 @@ class MessageItemFactory @Inject constructor(
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
}
val userId = if (locationContent.isGenericLocation()) null else informationData.senderId
return MessageLocationItem_()
.attributes(attributes)
.locationUrl(locationUrl)
.userId(informationData.senderId)
.userId(userId)
.locationPinProvider(locationPinProvider)
.highlighted(highlight)
.leftGuideline(avatarSizeProvider.leftGuideline)

View File

@ -45,7 +45,17 @@ class LocationPinProvider @Inject constructor(
GlideApp.with(context)
}
fun create(userId: String, callback: (Drawable) -> Unit) {
/**
* Creates a pin drawable. If userId is null then a generic pin drawable will be created.
* @param userId userId that will be used to retrieve user avatar
* @param callback Pin drawable will be sent through the callback
*/
fun create(userId: String?, callback: (Drawable) -> Unit) {
if (userId == null) {
callback(ContextCompat.getDrawable(context, R.drawable.ic_location_pin)!!)
return
}
if (cache.contains(userId)) {
callback(cache[userId]!!)
return

View File

@ -41,14 +41,13 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
renderSendState(holder.view, null)
val location = locationUrl ?: return
val locationOwnerId = userId ?: return
GlideApp.with(holder.staticMapImageView)
.load(location)
.apply(RequestOptions.centerCropTransform())
.into(holder.staticMapImageView)
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
locationPinProvider?.create(userId) { pinDrawable ->
GlideApp.with(holder.staticMapPinImageView)
.load(pinDrawable)
.into(holder.staticMapPinImageView)

View File

@ -18,6 +18,7 @@ package im.vector.app.features.location
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 USER_PIN_NAME = "USER_PIN_NAME"
const val INITIAL_MAP_ZOOM_IN_PREVIEW = 15.0
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0

View File

@ -121,7 +121,7 @@ class LocationPreviewFragment @Inject constructor(
MapState(
zoomOnlyOnce = true,
pinLocationData = location,
pinId = args.locationOwnerId,
pinId = args.locationOwnerId ?: USER_PIN_NAME,
pinDrawable = pinDrawable
)
)

View File

@ -30,7 +30,7 @@ data class LocationSharingArgs(
val roomId: String,
val mode: LocationSharingMode,
val initialLocationData: LocationData?,
val locationOwnerId: String
val locationOwnerId: String?
) : Parcelable
@AndroidEntryPoint

View File

@ -118,8 +118,4 @@ class LocationSharingFragment @Inject constructor(
views.mapView.render(state.toMapState())
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
}
companion object {
const val USER_PIN_NAME = "USER_PIN_NAME"
}
}

View File

@ -42,6 +42,6 @@ data class LocationSharingViewState(
fun LocationSharingViewState.toMapState() = MapState(
zoomOnlyOnce = true,
pinLocationData = lastKnownLocation,
pinId = LocationSharingFragment.USER_PIN_NAME,
pinId = USER_PIN_NAME,
pinDrawable = pinDrawable
)

View File

@ -545,7 +545,7 @@ class DefaultNavigator @Inject constructor(
roomId: String,
mode: LocationSharingMode,
initialLocationData: LocationData?,
locationOwnerId: String) {
locationOwnerId: String?) {
val intent = LocationSharingActivity.getIntent(
context,
LocationSharingArgs(roomId = roomId, mode = mode, initialLocationData = initialLocationData, locationOwnerId = locationOwnerId)

View File

@ -161,5 +161,5 @@ interface Navigator {
roomId: String,
mode: LocationSharingMode,
initialLocationData: LocationData?,
locationOwnerId: String)
locationOwnerId: String?)
}

View File

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="51dp"
android:height="54dp"
android:viewportWidth="51"
android:viewportHeight="54">
<path
android:pathData="M27.2956,44.2191C37.5577,42.7292 45.4403,33.8952 45.4403,23.2202C45.4403,11.5006 35.9397,2 24.2202,2C12.5006,2 3,11.5006 3,23.2202C3,33.8953 10.8827,42.7293 21.1449,44.2191L24.2202,47.1784L27.2956,44.2191Z"
android:fillColor="#0DBD8B"
android:fillType="evenOdd"/>
<path
android:pathData="M23.8041,15.073C20.5837,15.073 17.979,17.7486 17.979,21.0567C17.979,24.6213 21.6572,29.5365 23.1717,31.4085C23.5046,31.8188 24.112,31.8188 24.4449,31.4085C25.9511,29.5365 29.6293,24.6213 29.6293,21.0567C29.6293,17.7486 27.0246,15.073 23.8041,15.073ZM23.8041,23.1937C22.6558,23.1937 21.7237,22.2364 21.7237,21.0567C21.7237,19.8771 22.6558,18.9197 23.8041,18.9197C24.9525,18.9197 25.8846,19.8771 25.8846,21.0567C25.8846,22.2364 24.9525,23.1937 23.8041,23.1937Z"
android:fillColor="#ffffff"/>
</vector>

View File

@ -19,6 +19,9 @@ package im.vector.app.features.location
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldBeNull
import org.junit.Test
import org.matrix.android.sdk.api.session.room.model.message.LocationAsset
import org.matrix.android.sdk.api.session.room.model.message.LocationAssetType
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
class LocationDataTest {
@Test
@ -57,4 +60,16 @@ class LocationDataTest {
parseGeo("ge o:12.34,56.78;13.56").shouldBeNull()
parseGeo("geo :12.34,56.78;13.56").shouldBeNull()
}
@Test
fun genericLocationTest() {
val contentWithNullAsset = MessageLocationContent(body = "", geoUri = "", locationAsset = null)
contentWithNullAsset.isGenericLocation() shouldBeEqualTo(false)
val contentWithNullAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = null))
contentWithNullAssetType.isGenericLocation() shouldBeEqualTo(true)
val contentWithSelfAssetType = MessageLocationContent(body = "", geoUri = "", locationAsset = LocationAsset(type = LocationAssetType.SELF))
contentWithSelfAssetType.isGenericLocation() shouldBeEqualTo(false)
}
}