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>