From 40e92842ea8f564a9afcfb510a5c3aaa4aa216d3 Mon Sep 17 00:00:00 2001 From: Maxime Naturel <maxime.naturel@niji.fr> Date: Tue, 8 Mar 2022 11:49:59 +0100 Subject: [PATCH] Show icon to reset map to user location --- .../ui-styles/src/main/res/values/dimens.xml | 3 ++ .../location/LocationSharingFragment.kt | 12 +++++- .../location/LocationSharingViewModel.kt | 5 ++- .../app/features/location/MapTilerMapView.kt | 42 +++++++++++++++++-- vector/src/main/res/drawable/btn_locate.xml | 17 ++++++++ vector/src/main/res/drawable/ic_locate.xml | 9 ++++ vector/src/main/res/values/strings.xml | 1 + 7 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 vector/src/main/res/drawable/btn_locate.xml create mode 100644 vector/src/main/res/drawable/ic_locate.xml diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index 6737f4faf1..08fe557f7c 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -64,4 +64,7 @@ <!-- Location sharing --> <dimen name="location_sharing_option_default_padding">10dp</dimen> + <dimen name="location_sharing_locate_btn_margin_vertical">16dp</dimen> + <dimen name="location_sharing_locate_btn_margin_horizontal">12dp</dimen> + <dimen name="location_sharing_compass_btn_margin_horizontal">8dp</dimen> </resources> diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt index fb86033065..dc28c2dba9 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt @@ -67,10 +67,12 @@ class LocationSharingFragment @Inject constructor( lifecycleScope.launchWhenCreated { views.mapView.initialize( url = urlMapProvider.getMapUrl(), + showLocateBtn = true, locationTargetChangeListener = this@LocationSharingFragment ) } + initLocateBtn() initOptionsPicker() viewModel.observeViewEvents { @@ -141,12 +143,18 @@ class LocationSharingFragment @Inject constructor( .show() } + private fun initLocateBtn() { + views.mapView.locateBtn.setOnClickListener { + // TODO retrieve user location and zoom to this location + } + } + private fun initOptionsPicker() { // TODO // reset map to user location when clicking on reset icon + // changes in the event sent when this is a pinned location + // changes in the parsing of events when receiving pinned location: since we may present a different UI // unit tests - // need changes in the event sent when this is a pin drop location? - // need changes in the parsing of events when receiving pin drop location?: should it be shown with user avatar or with pin? // set no option at start views.shareLocationOptionsPicker.render() views.shareLocationOptionsPicker.optionPinned.debouncedClicks { diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt index b721adb93f..5ad762adff 100644 --- a/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt @@ -37,6 +37,9 @@ import kotlinx.coroutines.launch import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.util.toMatrixItem +/** + * Sampling period to compare target location and user location. + */ private const val TARGET_LOCATION_CHANGE_SAMPLING_PERIOD_IN_MS = 100L class LocationSharingViewModel @AssistedInject constructor( @@ -122,7 +125,7 @@ class LocationSharingViewModel @AssistedInject constructor( } private fun handlePinnedLocationSharingAction(action: LocationSharingAction.PinnedLocationSharingAction) { - // TODO check if we can use the same api than for user location? + // TODO confirm how to differentiate the user location and pinned location events? shareLocation(action.locationData) } diff --git a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt index 903b88423a..1c6a20aadf 100644 --- a/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt +++ b/vector/src/main/java/im/vector/app/features/location/MapTilerMapView.kt @@ -18,7 +18,12 @@ package im.vector.app.features.location import android.content.Context import android.util.AttributeSet +import android.view.Gravity +import android.widget.ImageView import androidx.core.content.ContextCompat +import androidx.core.view.marginBottom +import androidx.core.view.marginTop +import androidx.core.view.updateLayoutParams import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.maps.MapView @@ -47,16 +52,24 @@ class MapTilerMapView @JvmOverloads constructor( private val userLocationDrawable by lazy { ContextCompat.getDrawable(context, R.drawable.ic_location_user) } + val locateBtn by lazy { createLocateBtn() } private var mapRefs: MapRefs? = null private var initZoomDone = false /** * For location fragments */ - fun initialize(url: String, locationTargetChangeListener: LocationTargetChangeListener? = null) { + fun initialize( + url: String, + showLocateBtn: Boolean = false, // TODO transform into xml attribute + locationTargetChangeListener: LocationTargetChangeListener? = null + ) { Timber.d("## Location: initialize") getMapAsync { map -> initMapStyle(map, url) + if (showLocateBtn) { + showLocateBtn(map) + } notifyLocationOfMapCenter(locationTargetChangeListener) listenCameraMove(map, locationTargetChangeListener) } @@ -86,6 +99,30 @@ class MapTilerMapView @JvmOverloads constructor( } } + private fun createLocateBtn(): ImageView = + ImageView(context).apply { + setImageDrawable(ContextCompat.getDrawable(context, R.drawable.btn_locate)) + contentDescription = context.getString(R.string.a11y_location_share_locate_btn) + layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) + updateLayoutParams<MarginLayoutParams> { + val marginHorizontal = context.resources.getDimensionPixelOffset(R.dimen.location_sharing_locate_btn_margin_horizontal) + val marginVertical = context.resources.getDimensionPixelOffset(R.dimen.location_sharing_locate_btn_margin_vertical) + setMargins(marginHorizontal, marginVertical, marginHorizontal, marginVertical) + } + updateLayoutParams<LayoutParams> { + gravity = Gravity.TOP or Gravity.END + } + } + + private fun showLocateBtn(map: MapboxMap) { + addView(locateBtn) + locateBtn.post { + val marginTop = locateBtn.height + locateBtn.marginTop + locateBtn.marginBottom + val marginRight = context.resources.getDimensionPixelOffset(R.dimen.location_sharing_compass_btn_margin_horizontal) + map.uiSettings.setCompassMargins(0, marginTop, marginRight, 0) + } + } + fun render(state: MapState) { val safeMapRefs = mapRefs ?: return Unit.also { pendingState = state @@ -93,7 +130,6 @@ class MapTilerMapView @JvmOverloads constructor( safeMapRefs.map.uiSettings.setLogoMargins(0, 0, 0, state.logoMarginBottom) - // TODO add reset to user location button // TODO check conflict of rendering with preview location in timeline val pinDrawable = state.pinDrawable ?: userLocationDrawable pinDrawable?.let { drawable -> @@ -110,7 +146,7 @@ class MapTilerMapView @JvmOverloads constructor( } safeMapRefs.symbolManager.deleteAll() - if(pinDrawable != null && state.showPin) { + if (pinDrawable != null && state.showPin) { safeMapRefs.symbolManager.create( SymbolOptions() .withLatLng(LatLng(locationData.latitude, locationData.longitude)) diff --git a/vector/src/main/res/drawable/btn_locate.xml b/vector/src/main/res/drawable/btn_locate.xml new file mode 100644 index 0000000000..583b3a97ea --- /dev/null +++ b/vector/src/main/res/drawable/btn_locate.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@color/palette_white" /> + <corners android:radius="4dp" /> + </shape> + </item> + + <item android:drawable="?selectableItemBackground" /> + <item + android:bottom="8dp" + android:drawable="@drawable/ic_locate" + android:left="8dp" + android:right="8dp" + android:top="8dp" /> +</layer-list> diff --git a/vector/src/main/res/drawable/ic_locate.xml b/vector/src/main/res/drawable/ic_locate.xml new file mode 100644 index 0000000000..784665fcdd --- /dev/null +++ b/vector/src/main/res/drawable/ic_locate.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="22dp" + android:height="22dp" + android:viewportWidth="22" + android:viewportHeight="22"> + <path + android:pathData="M11,7C8.79,7 7,8.79 7,11C7,13.21 8.79,15 11,15C13.21,15 15,13.21 15,11C15,8.79 13.21,7 11,7ZM19.94,10C19.48,5.83 16.17,2.52 12,2.06V1C12,0.45 11.55,0 11,0C10.45,0 10,0.45 10,1V2.06C5.83,2.52 2.52,5.83 2.06,10H1C0.45,10 0,10.45 0,11C0,11.55 0.45,12 1,12H2.06C2.52,16.17 5.83,19.48 10,19.94V21C10,21.55 10.45,22 11,22C11.55,22 12,21.55 12,21V19.94C16.17,19.48 19.48,16.17 19.94,12H21C21.55,12 22,11.55 22,11C22,10.45 21.55,10 21,10H19.94ZM11,18C7.13,18 4,14.87 4,11C4,7.13 7.13,4 11,4C14.87,4 18,7.13 18,11C18,14.87 14.87,18 11,18Z" + android:fillColor="#0DBD8B"/> +</vector> diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index 62f6c37428..bbb2bc7740 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2930,6 +2930,7 @@ <!-- TODO delete --> <string name="location_share" tools:ignore="UnusedResources">Share location</string> <string name="a11y_location_share_pin_on_map">Pin of selected location on map</string> + <string name="a11y_location_share_locate_btn">Zoom to current location</string> <string name="location_share_option_user_current">Share my current location</string> <string name="a11y_location_share_option_user_current_icon">Share my current location</string> <string name="location_share_option_user_live">Share live location</string>