mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 05:31:21 +03:00
Support generic location pin.
This commit is contained in:
parent
6213cc73c0
commit
37d35c9a7f
16 changed files with 68 additions and 17 deletions
|
@ -64,4 +64,12 @@ data class MessageLocationContent(
|
||||||
) : MessageContent {
|
) : MessageContent {
|
||||||
|
|
||||||
fun getBestGeoUri() = locationInfo?.geoUri ?: geoUri
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,9 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var locationPinProvider: LocationPinProvider? = null
|
var locationPinProvider: LocationPinProvider? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute
|
||||||
|
var locationOwnerId: String? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var movementMethod: MovementMethod? = null
|
var movementMethod: MovementMethod? = null
|
||||||
|
|
||||||
|
@ -109,7 +112,7 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
||||||
.apply(RequestOptions.centerCropTransform())
|
.apply(RequestOptions.centerCropTransform())
|
||||||
.into(holder.staticMapImageView)
|
.into(holder.staticMapImageView)
|
||||||
|
|
||||||
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
|
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
||||||
GlideApp.with(holder.staticMapPinImageView)
|
GlideApp.with(holder.staticMapPinImageView)
|
||||||
.load(pinDrawable)
|
.load(pinDrawable)
|
||||||
.into(holder.staticMapPinImageView)
|
.into(holder.staticMapPinImageView)
|
||||||
|
|
|
@ -613,13 +613,14 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) {
|
private fun handleShowLocationPreview(locationContent: MessageLocationContent, senderId: String) {
|
||||||
|
val isGenericLocation = locationContent.isGenericLocation()
|
||||||
navigator
|
navigator
|
||||||
.openLocationSharing(
|
.openLocationSharing(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
roomId = roomDetailArgs.roomId,
|
roomId = roomDetailArgs.roomId,
|
||||||
mode = LocationSharingMode.PREVIEW,
|
mode = LocationSharingMode.PREVIEW,
|
||||||
initialLocationData = locationContent.toLocationData(),
|
initialLocationData = locationContent.toLocationData(),
|
||||||
locationOwnerId = senderId
|
locationOwnerId = if (isGenericLocation) null else senderId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,10 +77,12 @@ class MessageActionsEpoxyController @Inject constructor(
|
||||||
val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL)
|
val formattedDate = dateFormatter.format(date, DateFormatKind.MESSAGE_DETAIL)
|
||||||
val body = state.messageBody.linkify(host.listener)
|
val body = state.messageBody.linkify(host.listener)
|
||||||
val bindingOptions = spanUtils.getBindingOptions(body)
|
val bindingOptions = spanUtils.getBindingOptions(body)
|
||||||
val locationUrl = state.timelineEvent()?.root?.getClearContent()
|
|
||||||
|
val locationContent = state.timelineEvent()?.root?.getClearContent()
|
||||||
?.toModel<MessageLocationContent>(catchError = true)
|
?.toModel<MessageLocationContent>(catchError = true)
|
||||||
?.toLocationData()
|
val locationUrl = locationContent?.toLocationData()
|
||||||
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
|
?.let { urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, 1200, 800) }
|
||||||
|
val locationOwnerId = if (locationContent?.isGenericLocation().orFalse()) null else state.informationData.matrixItem.id
|
||||||
|
|
||||||
bottomSheetMessagePreviewItem {
|
bottomSheetMessagePreviewItem {
|
||||||
id("preview")
|
id("preview")
|
||||||
|
@ -96,6 +98,7 @@ class MessageActionsEpoxyController @Inject constructor(
|
||||||
time(formattedDate)
|
time(formattedDate)
|
||||||
locationUrl(locationUrl)
|
locationUrl(locationUrl)
|
||||||
locationPinProvider(host.locationPinProvider)
|
locationPinProvider(host.locationPinProvider)
|
||||||
|
locationOwnerId(locationOwnerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send state
|
// Send state
|
||||||
|
|
|
@ -207,10 +207,12 @@ class MessageItemFactory @Inject constructor(
|
||||||
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
|
urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val userId = if (locationContent.isGenericLocation()) null else informationData.senderId
|
||||||
|
|
||||||
return MessageLocationItem_()
|
return MessageLocationItem_()
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
.locationUrl(locationUrl)
|
.locationUrl(locationUrl)
|
||||||
.userId(informationData.senderId)
|
.userId(userId)
|
||||||
.locationPinProvider(locationPinProvider)
|
.locationPinProvider(locationPinProvider)
|
||||||
.highlighted(highlight)
|
.highlighted(highlight)
|
||||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||||
|
|
|
@ -45,7 +45,17 @@ class LocationPinProvider @Inject constructor(
|
||||||
GlideApp.with(context)
|
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)) {
|
if (cache.contains(userId)) {
|
||||||
callback(cache[userId]!!)
|
callback(cache[userId]!!)
|
||||||
return
|
return
|
||||||
|
|
|
@ -41,14 +41,13 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
||||||
renderSendState(holder.view, null)
|
renderSendState(holder.view, null)
|
||||||
|
|
||||||
val location = locationUrl ?: return
|
val location = locationUrl ?: return
|
||||||
val locationOwnerId = userId ?: return
|
|
||||||
|
|
||||||
GlideApp.with(holder.staticMapImageView)
|
GlideApp.with(holder.staticMapImageView)
|
||||||
.load(location)
|
.load(location)
|
||||||
.apply(RequestOptions.centerCropTransform())
|
.apply(RequestOptions.centerCropTransform())
|
||||||
.into(holder.staticMapImageView)
|
.into(holder.staticMapImageView)
|
||||||
|
|
||||||
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
locationPinProvider?.create(userId) { pinDrawable ->
|
||||||
GlideApp.with(holder.staticMapPinImageView)
|
GlideApp.with(holder.staticMapPinImageView)
|
||||||
.load(pinDrawable)
|
.load(pinDrawable)
|
||||||
.into(holder.staticMapPinImageView)
|
.into(holder.staticMapPinImageView)
|
||||||
|
|
|
@ -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 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 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_PREVIEW = 15.0
|
||||||
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0
|
const val INITIAL_MAP_ZOOM_IN_TIMELINE = 17.0
|
||||||
|
|
|
@ -121,7 +121,7 @@ class LocationPreviewFragment @Inject constructor(
|
||||||
MapState(
|
MapState(
|
||||||
zoomOnlyOnce = true,
|
zoomOnlyOnce = true,
|
||||||
pinLocationData = location,
|
pinLocationData = location,
|
||||||
pinId = args.locationOwnerId,
|
pinId = args.locationOwnerId ?: USER_PIN_NAME,
|
||||||
pinDrawable = pinDrawable
|
pinDrawable = pinDrawable
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -30,7 +30,7 @@ data class LocationSharingArgs(
|
||||||
val roomId: String,
|
val roomId: String,
|
||||||
val mode: LocationSharingMode,
|
val mode: LocationSharingMode,
|
||||||
val initialLocationData: LocationData?,
|
val initialLocationData: LocationData?,
|
||||||
val locationOwnerId: String
|
val locationOwnerId: String?
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
|
|
@ -118,8 +118,4 @@ class LocationSharingFragment @Inject constructor(
|
||||||
views.mapView.render(state.toMapState())
|
views.mapView.render(state.toMapState())
|
||||||
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
|
views.shareLocationGpsLoading.isGone = state.lastKnownLocation != null
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val USER_PIN_NAME = "USER_PIN_NAME"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,6 @@ data class LocationSharingViewState(
|
||||||
fun LocationSharingViewState.toMapState() = MapState(
|
fun LocationSharingViewState.toMapState() = MapState(
|
||||||
zoomOnlyOnce = true,
|
zoomOnlyOnce = true,
|
||||||
pinLocationData = lastKnownLocation,
|
pinLocationData = lastKnownLocation,
|
||||||
pinId = LocationSharingFragment.USER_PIN_NAME,
|
pinId = USER_PIN_NAME,
|
||||||
pinDrawable = pinDrawable
|
pinDrawable = pinDrawable
|
||||||
)
|
)
|
||||||
|
|
|
@ -545,7 +545,7 @@ class DefaultNavigator @Inject constructor(
|
||||||
roomId: String,
|
roomId: String,
|
||||||
mode: LocationSharingMode,
|
mode: LocationSharingMode,
|
||||||
initialLocationData: LocationData?,
|
initialLocationData: LocationData?,
|
||||||
locationOwnerId: String) {
|
locationOwnerId: String?) {
|
||||||
val intent = LocationSharingActivity.getIntent(
|
val intent = LocationSharingActivity.getIntent(
|
||||||
context,
|
context,
|
||||||
LocationSharingArgs(roomId = roomId, mode = mode, initialLocationData = initialLocationData, locationOwnerId = locationOwnerId)
|
LocationSharingArgs(roomId = roomId, mode = mode, initialLocationData = initialLocationData, locationOwnerId = locationOwnerId)
|
||||||
|
|
|
@ -161,5 +161,5 @@ interface Navigator {
|
||||||
roomId: String,
|
roomId: String,
|
||||||
mode: LocationSharingMode,
|
mode: LocationSharingMode,
|
||||||
initialLocationData: LocationData?,
|
initialLocationData: LocationData?,
|
||||||
locationOwnerId: String)
|
locationOwnerId: String?)
|
||||||
}
|
}
|
||||||
|
|
13
vector/src/main/res/drawable/ic_location_pin.xml
Normal file
13
vector/src/main/res/drawable/ic_location_pin.xml
Normal 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>
|
|
@ -19,6 +19,9 @@ package im.vector.app.features.location
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldBeNull
|
import org.amshove.kluent.shouldBeNull
|
||||||
import org.junit.Test
|
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 {
|
class LocationDataTest {
|
||||||
@Test
|
@Test
|
||||||
|
@ -57,4 +60,16 @@ class LocationDataTest {
|
||||||
parseGeo("ge o:12.34,56.78;13.56").shouldBeNull()
|
parseGeo("ge o:12.34,56.78;13.56").shouldBeNull()
|
||||||
parseGeo("geo :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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue