From 90aad44edfc6beafe151c66dbeb2ca2db1bcc5f6 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 29 Apr 2022 16:28:34 +0200
Subject: [PATCH 01/45] Adding live location summary data into
 MessageInformationData

---
 .../helper/MessageInformationDataFactory.kt     | 17 ++++++++++++++---
 .../timeline/item/MessageInformationData.kt     | 12 +++++++++++-
 2 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
index f882840eee..8fb7aea7e4 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
@@ -21,6 +21,7 @@ import im.vector.app.core.date.VectorDateFormatter
 import im.vector.app.core.extensions.localDateTime
 import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
 import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
+import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
 import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
 import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
@@ -44,8 +45,7 @@ import org.matrix.android.sdk.api.session.room.timeline.hasBeenEdited
 import javax.inject.Inject
 
 /**
- * TODO Update this comment
- * This class compute if data of an event (such has avatar, display name, ...) should be displayed, depending on the previous event in the timeline
+ * This class is responsible of building extra information data associated to a given event.
  */
 class MessageInformationDataFactory @Inject constructor(private val session: Session,
                                                         private val dateFormatter: VectorDateFormatter,
@@ -119,7 +119,8 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
                 isFirstFromThisSender = isFirstFromThisSender,
                 isLastFromThisSender = isLastFromThisSender,
                 e2eDecoration = e2eDecoration,
-                sendStateDecoration = sendStateDecoration
+                sendStateDecoration = sendStateDecoration,
+                liveLocationShareSummaryData = getLiveLocationShareSummaryData(event)
         )
     }
 
@@ -188,6 +189,16 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
         }
     }
 
+    private fun getLiveLocationShareSummaryData(event: TimelineEvent): LiveLocationShareSummaryData? {
+        return event.annotations?.liveLocationShareAggregatedSummary?.let { summary ->
+            LiveLocationShareSummaryData(
+                    isActive = summary.isActive,
+                    endOfLiveTimestampAsMilliseconds = summary.endOfLiveTimestampAsMilliseconds,
+                    lastGeoUri = summary.lastLocationDataContent?.getBestLocationInfo()?.geoUri
+            )
+        }
+    }
+
     /**
      * Tiles type message never show the sender information (like verification request), so we should repeat it for next message
      * even if same sender
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
index 9620077fd8..8ad4034a32 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
@@ -20,6 +20,8 @@ import android.os.Parcelable
 import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
+import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
+import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.api.util.MatrixItem
 
@@ -42,7 +44,8 @@ data class MessageInformationData(
         val e2eDecoration: E2EDecoration = E2EDecoration.NONE,
         val sendStateDecoration: SendStateDecoration = SendStateDecoration.NONE,
         val isFirstFromThisSender: Boolean = false,
-        val isLastFromThisSender: Boolean = false
+        val isLastFromThisSender: Boolean = false,
+        val liveLocationShareSummaryData: LiveLocationShareSummaryData? = null,
 ) : Parcelable {
 
     val matrixItem: MatrixItem
@@ -98,6 +101,13 @@ data class PollVoteSummaryData(
         val percentage: Double = 0.0
 ) : Parcelable
 
+@Parcelize
+data class LiveLocationShareSummaryData(
+        val isActive: Boolean?,
+        val endOfLiveTimestampAsMilliseconds: Long?,
+        val lastGeoUri: String?,
+) : Parcelable
+
 enum class E2EDecoration {
     NONE,
     WARN_IN_CLEAR,

From 0561fe5b08fd52baee71b3ad3fd3c81d1289cfd6 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Fri, 29 Apr 2022 17:00:32 +0200
Subject: [PATCH 02/45] Using information data in message factory

---
 .../factory/LiveLocationMessageItemFactory.kt    | 16 ++++++++++------
 .../timeline/factory/MessageItemFactory.kt       |  2 +-
 2 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
index d233deffb8..3656b86b79 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
@@ -21,9 +21,11 @@ import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
+import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
 import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
 import javax.inject.Inject
 
@@ -34,20 +36,22 @@ class LiveLocationMessageItemFactory @Inject constructor(
 ) {
 
     fun create(
-            beaconInfoContent: MessageBeaconInfoContent,
+            liveLocationShareSummaryData: LiveLocationShareSummaryData?,
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
         // TODO handle location received and stopped states
+        // TODO create a dedicated ViewState
         return when {
-            isLiveRunning(beaconInfoContent) -> buildStartLiveItem(highlight, attributes)
-            else                             -> null
+            liveLocationShareSummaryData == null        -> null
+            isLiveRunning(liveLocationShareSummaryData) -> buildStartLiveItem(highlight, attributes)
+            else                                        -> null
         }
     }
 
-    private fun isLiveRunning(beaconInfoContent: MessageBeaconInfoContent): Boolean {
-        // TODO when we will use aggregatedSummary, check if the live has timed out as well
-        return beaconInfoContent.isLive.orFalse()
+    private fun isLiveRunning(liveLocationShareSummaryData: LiveLocationShareSummaryData): Boolean {
+        // TODO check if the live has timed out as well
+        return liveLocationShareSummaryData.isActive.orFalse()
     }
 
     private fun buildStartLiveItem(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index b960e2c6a9..953c45f07e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -216,7 +216,7 @@ class MessageItemFactory @Inject constructor(
                     buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
                 }
             }
-            is MessageBeaconInfoContent          -> liveLocationMessageItemFactory.create(messageContent, highlight, attributes)
+            is MessageBeaconInfoContent          -> liveLocationMessageItemFactory.create(informationData.liveLocationShareSummaryData, highlight, attributes)
             else                                 -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
         }
         return messageItem?.apply {

From d89d6bc1623f8df0d0f5943ec2bfd2c748d77997 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 09:13:05 +0200
Subject: [PATCH 03/45] Adding view state class

---
 .../factory/LiveLocationMessageItemFactory.kt         | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
index 3656b86b79..86a5847605 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
@@ -25,8 +25,8 @@ import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSu
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
 import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.extensions.orTrue
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
+import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
+import java.time.LocalDateTime
 import javax.inject.Inject
 
 class LiveLocationMessageItemFactory @Inject constructor(
@@ -68,4 +68,11 @@ class LiveLocationMessageItemFactory @Inject constructor(
                 .highlighted(highlight)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
     }
+
+    private sealed class LiveLocationShareViewState {
+        object Loading : LiveLocationShareViewState()
+        data class Running(val locationInfo: LocationInfo, val endOfLiveDateTime: LocalDateTime?) : LiveLocationShareViewState()
+        object Inactive : LiveLocationShareViewState()
+        object Unkwown : LiveLocationShareViewState()
+    }
 }

From 68a44c4cc77e85bbfd983b4aee3043a2dd7fe40d Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 09:13:59 +0200
Subject: [PATCH 04/45] Renaming message item factory

---
 ...eItemFactory.kt => LiveLocationShareMessageItemFactory.kt} | 2 +-
 .../home/room/detail/timeline/factory/MessageItemFactory.kt   | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)
 rename vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/{LiveLocationMessageItemFactory.kt => LiveLocationShareMessageItemFactory.kt} (98%)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
similarity index 98%
rename from vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
rename to vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index 86a5847605..f243da7c81 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -29,7 +29,7 @@ import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
 import java.time.LocalDateTime
 import javax.inject.Inject
 
-class LiveLocationMessageItemFactory @Inject constructor(
+class LiveLocationShareMessageItemFactory @Inject constructor(
         private val dimensionConverter: DimensionConverter,
         private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
         private val avatarSizeProvider: AvatarSizeProvider,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 953c45f07e..6f22b3eff4 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -148,7 +148,7 @@ class MessageItemFactory @Inject constructor(
         private val locationPinProvider: LocationPinProvider,
         private val vectorPreferences: VectorPreferences,
         private val urlMapProvider: UrlMapProvider,
-        private val liveLocationMessageItemFactory: LiveLocationMessageItemFactory,
+        private val liveLocationShareMessageItemFactory: LiveLocationShareMessageItemFactory,
 ) {
 
     // TODO inject this properly?
@@ -216,7 +216,7 @@ class MessageItemFactory @Inject constructor(
                     buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
                 }
             }
-            is MessageBeaconInfoContent          -> liveLocationMessageItemFactory.create(informationData.liveLocationShareSummaryData, highlight, attributes)
+            is MessageBeaconInfoContent          -> liveLocationShareMessageItemFactory.create(informationData.liveLocationShareSummaryData, highlight, attributes)
             else                                 -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
         }
         return messageItem?.apply {

From d44a6c50f1f4b5623c4688103a8621eaf139150c Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 09:47:07 +0200
Subject: [PATCH 05/45] Fixes in DateProvider

---
 .../vector/app/core/resources/DateProvider.kt | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt b/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
index 30cb1dcae4..6762bd68da 100644
--- a/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
+++ b/vector/src/main/java/im/vector/app/core/resources/DateProvider.kt
@@ -19,27 +19,30 @@ package im.vector.app.core.resources
 import org.threeten.bp.Instant
 import org.threeten.bp.LocalDateTime
 import org.threeten.bp.ZoneId
+import org.threeten.bp.ZoneOffset
 
 object DateProvider {
 
-    private val zoneId = ZoneId.systemDefault()
-    private val zoneOffset by lazy {
-        val now = currentLocalDateTime()
-        zoneId.rules.getOffset(now)
-    }
+    // recompute the zoneId each time we access it to handle change of timezones
+    private val defaultZoneId: ZoneId
+        get() = ZoneId.systemDefault()
+
+    // recompute the zoneOffset each time we access it to handle change of timezones
+    private val defaultZoneOffset: ZoneOffset
+        get() = defaultZoneId.rules.getOffset(currentLocalDateTime())
 
     fun toLocalDateTime(timestamp: Long?): LocalDateTime {
         val instant = Instant.ofEpochMilli(timestamp ?: 0)
-        return LocalDateTime.ofInstant(instant, zoneId)
+        return LocalDateTime.ofInstant(instant, defaultZoneId)
     }
 
     fun currentLocalDateTime(): LocalDateTime {
         val instant = Instant.now()
-        return LocalDateTime.ofInstant(instant, zoneId)
+        return LocalDateTime.ofInstant(instant, defaultZoneId)
     }
 
     fun toTimestamp(localDateTime: LocalDateTime): Long {
-        return localDateTime.toInstant(zoneOffset).toEpochMilli()
+        return localDateTime.toInstant(defaultZoneOffset).toEpochMilli()
     }
 }
 

From 431d86166ff0ffa93fbe73d645856fd098e7c185 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 09:47:48 +0200
Subject: [PATCH 06/45] Building correct item depending on the state of the
 live

---
 .../LiveLocationShareMessageItemFactory.kt    | 56 ++++++++++++++-----
 1 file changed, 41 insertions(+), 15 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index f243da7c81..760ec92cd6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.home.room.detail.timeline.factory
 
 import im.vector.app.core.epoxy.VectorEpoxyModel
+import im.vector.app.core.resources.DateProvider
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
@@ -25,8 +26,8 @@ import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSu
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
 import org.matrix.android.sdk.api.extensions.orFalse
-import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
-import java.time.LocalDateTime
+import org.threeten.bp.LocalDateTime
+import timber.log.Timber
 import javax.inject.Inject
 
 class LiveLocationShareMessageItemFactory @Inject constructor(
@@ -40,21 +41,15 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
-        // TODO handle location received and stopped states
-        // TODO create a dedicated ViewState
-        return when {
-            liveLocationShareSummaryData == null        -> null
-            isLiveRunning(liveLocationShareSummaryData) -> buildStartLiveItem(highlight, attributes)
-            else                                        -> null
+        return when (getViewState(liveLocationShareSummaryData)) {
+            LiveLocationShareViewState.Loading    -> buildLoadingItem(highlight, attributes)
+            LiveLocationShareViewState.Inactive   -> buildInactiveItem()
+            is LiveLocationShareViewState.Running -> buildRunningItem()
+            LiveLocationShareViewState.Unkwown    -> null
         }
     }
 
-    private fun isLiveRunning(liveLocationShareSummaryData: LiveLocationShareSummaryData): Boolean {
-        // TODO check if the live has timed out as well
-        return liveLocationShareSummaryData.isActive.orFalse()
-    }
-
-    private fun buildStartLiveItem(
+    private fun buildLoadingItem(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): MessageLiveLocationStartItem {
@@ -69,9 +64,40 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
                 .leftGuideline(avatarSizeProvider.leftGuideline)
     }
 
+    private fun buildRunningItem() = null
+
+    private fun buildInactiveItem() = null
+
+    private fun getViewState(liveLocationShareSummaryData: LiveLocationShareSummaryData?): LiveLocationShareViewState {
+        return when {
+            liveLocationShareSummaryData?.isActive == null                                                   -> LiveLocationShareViewState.Unkwown
+            liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
+            liveLocationShareSummaryData.isActive.not() || isLiveTimedOut(liveLocationShareSummaryData)      -> LiveLocationShareViewState.Inactive
+            else                                                                                             ->
+                LiveLocationShareViewState.Running(
+                        liveLocationShareSummaryData.lastGeoUri.orEmpty(),
+                        getEndOfLiveDateTime(liveLocationShareSummaryData)
+                )
+        }.also { viewState -> Timber.d("computed viewState: $viewState") }
+    }
+
+    private fun isLiveTimedOut(liveLocationShareSummaryData: LiveLocationShareSummaryData): Boolean {
+        return getEndOfLiveDateTime(liveLocationShareSummaryData)
+                ?.let { endOfLive ->
+                    // this will only cover users with different timezones but not users with manually time set
+                    val now = LocalDateTime.now()
+                    now.isAfter(endOfLive)
+                }
+                .orFalse()
+    }
+
+    private fun getEndOfLiveDateTime(liveLocationShareSummaryData: LiveLocationShareSummaryData): LocalDateTime? {
+        return liveLocationShareSummaryData.endOfLiveTimestampAsMilliseconds?.let { DateProvider.toLocalDateTime(timestamp = it) }
+    }
+
     private sealed class LiveLocationShareViewState {
         object Loading : LiveLocationShareViewState()
-        data class Running(val locationInfo: LocationInfo, val endOfLiveDateTime: LocalDateTime?) : LiveLocationShareViewState()
+        data class Running(val lastGeoUri: String, val endOfLiveDateTime: LocalDateTime?) : LiveLocationShareViewState()
         object Inactive : LiveLocationShareViewState()
         object Unkwown : LiveLocationShareViewState()
     }

From df1ba8ec88f6295b6df663071a113ccf8ee52a10 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 4 Apr 2022 18:14:51 +0200
Subject: [PATCH 07/45] Custom view for the banner

---
 .../src/main/res/values/styles_location.xml   |  8 +++
 .../live/LocationLiveMessageBannerView.kt     | 40 +++++++++++
 .../view_location_live_message_banner.xml     | 67 +++++++++++++++++++
 vector/src/main/res/values/strings.xml        |  1 +
 4 files changed, 116 insertions(+)
 create mode 100644 vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
 create mode 100644 vector/src/main/res/layout/view_location_live_message_banner.xml

diff --git a/library/ui-styles/src/main/res/values/styles_location.xml b/library/ui-styles/src/main/res/values/styles_location.xml
index 5563d28342..8f76ed469e 100644
--- a/library/ui-styles/src/main/res/values/styles_location.xml
+++ b/library/ui-styles/src/main/res/values/styles_location.xml
@@ -8,4 +8,12 @@
         <item name="android:gravity">center</item>
     </style>
 
+    <style name="Widget.Vector.Button.Text.LocationLive">
+        <item name="android:background">?selectableItemBackground</item>
+        <item name="android:textAppearance">@style/TextAppearance.Vector.Body.Medium</item>
+        <item name="android:textColor">?colorError</item>
+        <item name="android:padding">0dp</item>
+        <item name="android:gravity">center</item>
+    </style>
+
 </resources>
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
new file mode 100644
index 0000000000..95805d5b7d
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.location.live
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.Button
+import androidx.constraintlayout.widget.ConstraintLayout
+import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
+import im.vector.app.databinding.ViewLocationLiveStatusBinding
+
+class LocationLiveMessageBannerView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val binding = ViewLocationLiveMessageBannerBinding.inflate(
+            LayoutInflater.from(context),
+            this
+    )
+
+    val stopButton: Button
+        get() = binding.locationLiveMessageBannerStop
+}
diff --git a/vector/src/main/res/layout/view_location_live_message_banner.xml b/vector/src/main/res/layout/view_location_live_message_banner.xml
new file mode 100644
index 0000000000..f3345bac05
--- /dev/null
+++ b/vector/src/main/res/layout/view_location_live_message_banner.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+
+    <View
+        android:id="@+id/locationLiveMessageBannerBackground"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:alpha="0.85"
+        android:background="?colorSurface"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/locationLiveMessageBannerIcon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginHorizontal="8dp"
+        android:background="@drawable/circle"
+        android:backgroundTint="?vctr_live_location"
+        android:padding="3dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="@id/locationLiveMessageBannerBackground"
+        app:layout_constraintTop_toTopOf="parent"
+        app:srcCompat="@drawable/ic_attachment_location_live_white"
+        tools:ignore="ContentDescription" />
+
+    <TextView
+        android:id="@+id/locationLiveMessageBannerTitle"
+        style="@style/Widget.Vector.TextView.Caption"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="8dp"
+        android:text="@string/location_share_live_enabled"
+        android:textColor="?colorOnSurface"
+        app:layout_constraintBottom_toTopOf="@id/locationLiveMessageBannerSubTitle"
+        app:layout_constraintStart_toEndOf="@id/locationLiveMessageBannerIcon"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed" />
+
+    <TextView
+        android:id="@+id/locationLiveMessageBannerSubTitle"
+        style="@style/Widget.Vector.TextView.Caption"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="?vctr_content_secondary"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="@id/locationLiveMessageBannerTitle"
+        app:layout_constraintTop_toBottomOf="@id/locationLiveMessageBannerTitle"
+        tools:text="9min left" />
+
+    <Button
+        android:id="@+id/locationLiveMessageBannerStop"
+        style="@style/Widget.Vector.Button.Text.LocationLive"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginHorizontal="8dp"
+        android:text="@string/location_share_live_stop_long_version"
+        app:layout_constraintBottom_toBottomOf="@id/locationLiveMessageBannerBackground"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="@id/locationLiveMessageBannerBackground" />
+</merge>
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index d5bc322546..f137592c60 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -3011,6 +3011,7 @@
     <string name="location_share_live_enabled">Live location enabled</string>
     <string name="location_share_live_started">Loading live location…</string>
     <string name="location_share_live_stop">Stop</string>
+    <string name="location_share_live_stop_long_version">Stop sharing</string>
     <string name="live_location_sharing_notification_title">${app_name} Live Location</string>
     <string name="live_location_sharing_notification_description">Location sharing is in progress</string>
 

From a412b2128d583f7ec375268f7d598c89583656eb Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Apr 2022 15:13:01 +0200
Subject: [PATCH 08/45] Rendering method

---
 .../im/vector/app/core/utils/TextUtils.kt     | 55 ++++++++++++++++++-
 .../live/LocationLiveMessageBannerView.kt     | 20 ++++++-
 vector/src/main/res/values/strings.xml        |  9 +++
 3 files changed, 80 insertions(+), 4 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index 992a85679c..db2009a620 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -19,11 +19,15 @@ package im.vector.app.core.utils
 import android.content.Context
 import android.os.Build
 import android.text.format.Formatter
+import im.vector.app.R
 import org.threeten.bp.Duration
 import java.util.TreeMap
 
 object TextUtils {
 
+    private const val MINUTES_PER_HOUR = 60
+    private const val SECONDS_PER_MINUTE = 60
+
     private val suffixes = TreeMap<Int, String>().also {
         it[1000] = "k"
         it[1000000] = "M"
@@ -71,13 +75,58 @@ object TextUtils {
     }
 
     fun formatDuration(duration: Duration): String {
-        val hours = duration.seconds / 3600
-        val minutes = (duration.seconds % 3600) / 60
-        val seconds = duration.seconds % 60
+        val hours = getHours(duration)
+        val minutes = getMinutes(duration)
+        val seconds = getSeconds(duration)
         return if (hours > 0) {
             String.format("%d:%02d:%02d", hours, minutes, seconds)
         } else {
             String.format("%02d:%02d", minutes, seconds)
         }
     }
+
+    fun formatDurationWithUnits(context: Context, duration: Duration): String {
+        val hours = getHours(duration)
+        val minutes = getMinutes(duration)
+        val seconds = getSeconds(duration)
+        val builder = StringBuilder()
+        // TODO do we need Locale ? test with different language setting
+        when {
+            hours > 0   -> {
+                appendHours(context, builder, hours)
+                builder.append(" ")
+                appendMinutes(context, builder, minutes)
+                builder.append(" ")
+                appendSeconds(context, builder, seconds)
+            }
+            minutes > 0 -> {
+                appendMinutes(context, builder, minutes)
+                builder.append(" ")
+                appendSeconds(context, builder, seconds)
+            }
+            else        -> {
+                appendSeconds(context, builder, seconds)
+            }
+        }
+
+        return builder.toString()
+    }
+
+    private fun appendHours(context: Context, builder: StringBuilder, hours: Int) {
+        builder.append(context.resources.getQuantityString(R.plurals.time_unit_hour_short, hours))
+    }
+
+    private fun appendMinutes(context: Context, builder: StringBuilder, minutes: Int) {
+        builder.append(minutes)
+        builder.append(context.getString(R.string.time_unit_minute_short))
+    }
+
+    private fun appendSeconds(context: Context, builder: StringBuilder, seconds: Int) {
+        builder.append(seconds)
+        builder.append(context.getString(R.string.time_unit_second_short))
+    }
+
+    private fun getHours(duration: Duration): Int = duration.toHours().toInt()
+    private fun getMinutes(duration: Duration): Int = duration.toMinutes().toInt() % MINUTES_PER_HOUR
+    private fun getSeconds(duration: Duration): Int = (duration.seconds % SECONDS_PER_MINUTE).toInt()
 }
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 95805d5b7d..12fb0db4ba 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -20,9 +20,18 @@ import android.content.Context
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.widget.Button
+import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.isVisible
+import im.vector.app.R
+import im.vector.app.core.utils.TextUtils
 import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
-import im.vector.app.databinding.ViewLocationLiveStatusBinding
+import org.threeten.bp.Duration
+
+data class LocationLiveMessageBannerViewState(
+        val isStopButtonVisible: Boolean,
+        val remainingTimeInMillis: Long
+)
 
 class LocationLiveMessageBannerView @JvmOverloads constructor(
         context: Context,
@@ -37,4 +46,13 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
 
     val stopButton: Button
         get() = binding.locationLiveMessageBannerStop
+
+    private val subTitle: TextView
+        get() = binding.locationLiveMessageBannerSubTitle
+
+    fun render(viewState: LocationLiveMessageBannerViewState) {
+        stopButton.isVisible = viewState.isStopButtonVisible
+        val duration = Duration.ofMillis(viewState.remainingTimeInMillis.coerceAtLeast(0L))
+        subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+    }
 }
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index f137592c60..b73bb47690 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -322,6 +322,14 @@
     <string name="start_chatting">Start Chatting</string>
     <string name="spaces">Spaces</string>
 
+    <!-- Time units -->
+    <plurals name="time_unit_hour_short">
+        <item quantity="one">hour</item>
+        <item quantity="other">hours</item>
+    </plurals>
+    <string name="time_unit_minute_short">min</string>
+    <string name="time_unit_second_short">sec</string>
+
     <!-- Permissions denied forever -->
     <string name="denied_permission_generic">Some permissions are missing to perform this action, please grant the permissions from the system settings.</string>
     <string name="denied_permission_camera">To perform this action, please grant the Camera permission from the system settings.</string>
@@ -3012,6 +3020,7 @@
     <string name="location_share_live_started">Loading live location…</string>
     <string name="location_share_live_stop">Stop</string>
     <string name="location_share_live_stop_long_version">Stop sharing</string>
+    <string name="location_share_live_remaining_time">%1$s left</string>
     <string name="live_location_sharing_notification_title">${app_name} Live Location</string>
     <string name="live_location_sharing_notification_description">Location sharing is in progress</string>
 

From 3acc139307f29597de925649cbd9cb40208da947 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Apr 2022 16:07:19 +0200
Subject: [PATCH 09/45] Fix format of the duration

---
 vector/src/main/java/im/vector/app/core/utils/TextUtils.kt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index db2009a620..bb197b2e9d 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -90,7 +90,6 @@ object TextUtils {
         val minutes = getMinutes(duration)
         val seconds = getSeconds(duration)
         val builder = StringBuilder()
-        // TODO do we need Locale ? test with different language setting
         when {
             hours > 0   -> {
                 appendHours(context, builder, hours)
@@ -108,11 +107,11 @@ object TextUtils {
                 appendSeconds(context, builder, seconds)
             }
         }
-
         return builder.toString()
     }
 
     private fun appendHours(context: Context, builder: StringBuilder, hours: Int) {
+        builder.append(hours)
         builder.append(context.resources.getQuantityString(R.plurals.time_unit_hour_short, hours))
     }
 

From bbec3a7c2ef540209b4d33b75edb502a468e97d6 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 5 Apr 2022 16:11:01 +0200
Subject: [PATCH 10/45] Integration into location message item

---
 .../src/main/res/values/styles_location.xml   |  6 +-
 .../timeline/item/MessageLocationItem.kt      | 46 ++++++++++-
 .../live/LocationLiveMessageBannerView.kt     | 76 +++++++++++++++++--
 .../item_timeline_event_location_stub.xml     |  8 ++
 .../view_location_live_message_banner.xml     | 20 ++---
 vector/src/main/res/values/strings.xml        |  3 +-
 6 files changed, 137 insertions(+), 22 deletions(-)

diff --git a/library/ui-styles/src/main/res/values/styles_location.xml b/library/ui-styles/src/main/res/values/styles_location.xml
index 8f76ed469e..7571265241 100644
--- a/library/ui-styles/src/main/res/values/styles_location.xml
+++ b/library/ui-styles/src/main/res/values/styles_location.xml
@@ -2,14 +2,16 @@
 <resources>
 
     <style name="Widget.Vector.Button.Text.OnPrimary.LocationLive">
-        <item name="android:background">?selectableItemBackground</item>
+        <item name="android:foreground">?selectableItemBackground</item>
+        <item name="android:background">@android:color/transparent</item>
         <item name="android:textSize">12sp</item>
         <item name="android:padding">0dp</item>
         <item name="android:gravity">center</item>
     </style>
 
     <style name="Widget.Vector.Button.Text.LocationLive">
-        <item name="android:background">?selectableItemBackground</item>
+        <item name="android:foreground">?selectableItemBackground</item>
+        <item name="android:background">@android:color/transparent</item>
         <item name="android:textAppearance">@style/TextAppearance.Vector.Body.Medium</item>
         <item name="android:textColor">?colorError</item>
         <item name="android:padding">0dp</item>
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
index 1e2808afd8..cb823aae06 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
@@ -35,6 +35,8 @@ import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
 import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
 import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
+import im.vector.app.features.location.live.LocationLiveMessageBannerView
+import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
 abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
@@ -57,12 +59,17 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
     override fun bind(holder: Holder) {
         super.bind(holder)
         renderSendState(holder.view, null)
+        bindMap(holder)
+        bindLocationLiveBanner(holder)
+    }
+
+    private fun bindMap(holder: Holder) {
         val location = locationUrl ?: return
         val messageLayout = attributes.informationData.messageLayout
-        val dimensionConverter = DimensionConverter(holder.view.resources)
         val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
             messageLayout.cornersRadius.granularRoundedCorners()
         } else {
+            val dimensionConverter = DimensionConverter(holder.view.resources)
             RoundedCorners(dimensionConverter.dpToPx(8))
         }
         holder.staticMapImageView.updateLayoutParams {
@@ -88,9 +95,8 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
                                                  dataSource: DataSource?,
                                                  isFirstResource: Boolean): Boolean {
                         locationPinProvider?.create(userId) { pinDrawable ->
-                            GlideApp.with(holder.staticMapPinImageView)
-                                    .load(pinDrawable)
-                                    .into(holder.staticMapPinImageView)
+                            // we are not using Glide since it does not display it correctly when there is no user photo
+                            holder.staticMapPinImageView.setImageDrawable(pinDrawable)
                         }
                         holder.staticMapErrorTextView.isVisible = false
                         return false
@@ -100,12 +106,44 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
                 .into(holder.staticMapImageView)
     }
 
+    private fun bindLocationLiveBanner(holder: Holder) {
+        val messageLayout = attributes.informationData.messageLayout
+        val viewState = if (messageLayout is TimelineMessageLayout.Bubble) {
+            LocationLiveMessageBannerViewState.Emitter(
+                    remainingTimeInMillis = 4000 * 1000L,
+                    bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+                    bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+                    isStopButtonCenteredVertically = false
+            )
+        } else {
+            val dimensionConverter = DimensionConverter(holder.view.resources)
+            val cornerRadius = dimensionConverter.dpToPx(8).toFloat()
+//            LocationLiveMessageBannerViewState.Watcher(
+//                    bottomStartCornerRadiusInDp = cornerRadius,
+//                    bottomEndCornerRadiusInDp = cornerRadius,
+//                    formattedLocalTimeOfEndOfLive = "12:34",
+//            )
+            LocationLiveMessageBannerViewState.Emitter(
+                    remainingTimeInMillis = 4000 * 1000L,
+                    bottomStartCornerRadiusInDp = cornerRadius,
+                    bottomEndCornerRadiusInDp = cornerRadius,
+                    isStopButtonCenteredVertically = true
+            )
+        }
+        holder.locationLiveMessageBanner.isVisible = true
+        holder.locationLiveMessageBanner.render(viewState)
+
+        // TODO create a dedicated message Item per state: Start, Location, End? Check if inheritance is possible in Epoxy model
+        // TODO adjust Copyright map placement
+    }
+
     override fun getViewStubId() = STUB_ID
 
     class Holder : AbsMessageItem.Holder(STUB_ID) {
         val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
         val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
         val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView)
+        val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
     }
 
     companion object {
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 12fb0db4ba..989adc27b5 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -17,21 +17,42 @@
 package im.vector.app.features.location.live
 
 import android.content.Context
+import android.graphics.drawable.ColorDrawable
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.widget.Button
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.core.view.isVisible
+import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
 import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
 import im.vector.app.core.utils.TextUtils
 import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
+import im.vector.app.features.themes.ThemeUtils
 import org.threeten.bp.Duration
 
-data class LocationLiveMessageBannerViewState(
-        val isStopButtonVisible: Boolean,
-        val remainingTimeInMillis: Long
-)
+// TODO should it be moved to timeline.item package?
+sealed class LocationLiveMessageBannerViewState(
+        open val bottomStartCornerRadiusInDp: Float,
+        open val bottomEndCornerRadiusInDp: Float,
+) {
+
+    data class Emitter(
+            override val bottomStartCornerRadiusInDp: Float,
+            override val bottomEndCornerRadiusInDp: Float,
+            val remainingTimeInMillis: Long,
+            val isStopButtonCenteredVertically: Boolean
+    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+
+    data class Watcher(
+            override val bottomStartCornerRadiusInDp: Float,
+            override val bottomEndCornerRadiusInDp: Float,
+            val formattedLocalTimeOfEndOfLive: String,
+    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+}
 
 class LocationLiveMessageBannerView @JvmOverloads constructor(
         context: Context,
@@ -47,12 +68,57 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
     val stopButton: Button
         get() = binding.locationLiveMessageBannerStop
 
+    private val background: ImageView
+        get() = binding.locationLiveMessageBannerBackground
+
+    private val title: TextView
+        get() = binding.locationLiveMessageBannerTitle
+
     private val subTitle: TextView
         get() = binding.locationLiveMessageBannerSubTitle
 
     fun render(viewState: LocationLiveMessageBannerViewState) {
-        stopButton.isVisible = viewState.isStopButtonVisible
+        when (viewState) {
+            is LocationLiveMessageBannerViewState.Emitter -> renderEmitter(viewState)
+            is LocationLiveMessageBannerViewState.Watcher -> renderWatcher(viewState)
+        }
+
+        GlideApp.with(context)
+                .load(ColorDrawable(ThemeUtils.getColor(context, R.attr.colorSurface)))
+                .transform(GranularRoundedCorners(0f, 0f, viewState.bottomEndCornerRadiusInDp, viewState.bottomStartCornerRadiusInDp))
+                .into(background)
+    }
+
+    private fun renderEmitter(viewState: LocationLiveMessageBannerViewState.Emitter) {
+        stopButton.isVisible = true
+        title.text = context.getString(R.string.location_share_live_enabled)
         val duration = Duration.ofMillis(viewState.remainingTimeInMillis.coerceAtLeast(0L))
         subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+
+        val rootLayout: ConstraintLayout? = (binding.root as? ConstraintLayout)
+        rootLayout?.let { parentLayout ->
+            val constraintSet = ConstraintSet()
+            constraintSet.clone(rootLayout)
+
+            if (viewState.isStopButtonCenteredVertically) {
+                constraintSet.connect(
+                        R.id.locationLiveMessageBannerStop,
+                        ConstraintSet.BOTTOM,
+                        R.id.locationLiveMessageBannerBackground,
+                        ConstraintSet.BOTTOM,
+                        0
+                )
+            } else {
+                constraintSet.clear(R.id.locationLiveMessageBannerStop, ConstraintSet.BOTTOM)
+            }
+
+            constraintSet.applyTo(parentLayout)
+        }
+    }
+
+    private fun renderWatcher(viewState: LocationLiveMessageBannerViewState.Watcher) {
+        stopButton.isVisible = false
+        title.text = context.getString(R.string.location_share_live_view)
+        subTitle.text = context.getString(R.string.location_share_live_until, viewState.formattedLocalTimeOfEndOfLive)
     }
 }
diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
index f735c2b00e..036d5149b0 100644
--- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
@@ -45,4 +45,12 @@
         app:layout_constraintTop_toBottomOf="@id/staticMapPinImageView"
         tools:visibility="visible" />
 
+    <im.vector.app.features.location.live.LocationLiveMessageBannerView
+        android:id="@+id/locationLiveMessageBanner"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintStart_toStartOf="@id/staticMapImageView"
+        app:layout_constraintEnd_toEndOf="@id/staticMapImageView"
+        app:layout_constraintBottom_toBottomOf="@id/staticMapImageView" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/view_location_live_message_banner.xml b/vector/src/main/res/layout/view_location_live_message_banner.xml
index f3345bac05..42004a5b81 100644
--- a/vector/src/main/res/layout/view_location_live_message_banner.xml
+++ b/vector/src/main/res/layout/view_location_live_message_banner.xml
@@ -6,15 +6,16 @@
     android:layout_height="wrap_content"
     tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
 
-    <View
+    <ImageView
         android:id="@+id/locationLiveMessageBannerBackground"
         android:layout_width="0dp"
-        android:layout_height="48dp"
+        android:layout_height="50dp"
         android:alpha="0.85"
-        android:background="?colorSurface"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toTopOf="parent"
+        tools:background="?colorSurface"
+        tools:ignore="ContentDescription" />
 
     <ImageView
         android:id="@+id/locationLiveMessageBannerIcon"
@@ -36,7 +37,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="8dp"
-        android:text="@string/location_share_live_enabled"
+        tools:text="@string/location_share_live_enabled"
         android:textColor="?colorOnSurface"
         app:layout_constraintBottom_toTopOf="@id/locationLiveMessageBannerSubTitle"
         app:layout_constraintStart_toEndOf="@id/locationLiveMessageBannerIcon"
@@ -57,11 +58,10 @@
     <Button
         android:id="@+id/locationLiveMessageBannerStop"
         style="@style/Widget.Vector.Button.Text.LocationLive"
-        android:layout_width="wrap_content"
-        android:layout_height="0dp"
-        android:layout_marginHorizontal="8dp"
-        android:text="@string/location_share_live_stop_long_version"
-        app:layout_constraintBottom_toBottomOf="@id/locationLiveMessageBannerBackground"
+        android:layout_width="45dp"
+        android:layout_height="30dp"
+        android:text="@string/location_share_live_stop"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toBottomOf="@id/locationLiveMessageBannerBackground"
         app:layout_constraintTop_toTopOf="@id/locationLiveMessageBannerBackground" />
 </merge>
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index b73bb47690..00b29e83b3 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -3018,8 +3018,9 @@
     <string name="location_timeline_failed_to_load_map">Failed to load map</string>
     <string name="location_share_live_enabled">Live location enabled</string>
     <string name="location_share_live_started">Loading live location…</string>
+    <string name="location_share_live_view">View live location</string>
+    <string name="location_share_live_until">Live until %1$s</string>
     <string name="location_share_live_stop">Stop</string>
-    <string name="location_share_live_stop_long_version">Stop sharing</string>
     <string name="location_share_live_remaining_time">%1$s left</string>
     <string name="live_location_sharing_notification_title">${app_name} Live Location</string>
     <string name="live_location_sharing_notification_description">Location sharing is in progress</string>

From 8c012145f921a463ed5c14da1a574e7ab06453db Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 11 Apr 2022 15:46:30 +0200
Subject: [PATCH 11/45] Creating a dedicated live location item

---
 .../timeline/item/AbsMessageLocationItem.kt   | 115 +++++++++++++++
 .../timeline/item/MessageLiveLocationItem.kt  |  70 +++++++++
 .../timeline/item/MessageLocationItem.kt      | 133 +-----------------
 .../item_timeline_event_location_stub.xml     |   6 +-
 4 files changed, 192 insertions(+), 132 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
new file mode 100644
index 0000000000..b95fd0f4e1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2021 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.graphics.drawable.Drawable
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+import com.airbnb.epoxy.EpoxyAttribute
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.Target
+import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
+
+abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder> : AbsMessageItem<H>() {
+
+    @EpoxyAttribute
+    var locationUrl: String? = null
+
+    @EpoxyAttribute
+    var userId: String? = null
+
+    @EpoxyAttribute
+    var mapWidth: Int = 0
+
+    @EpoxyAttribute
+    var mapHeight: Int = 0
+
+    @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+    var locationPinProvider: LocationPinProvider? = null
+
+    override fun bind(holder: H) {
+        super.bind(holder)
+        renderSendState(holder.view, null)
+        bindMap(holder)
+    }
+
+    private fun bindMap(holder: Holder) {
+        val location = locationUrl ?: return
+        val messageLayout = attributes.informationData.messageLayout
+        val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+            messageLayout.cornersRadius.granularRoundedCorners()
+        } else {
+            val dimensionConverter = DimensionConverter(holder.view.resources)
+            RoundedCorners(dimensionConverter.dpToPx(8))
+        }
+        holder.staticMapImageView.updateLayoutParams {
+            width = mapWidth
+            height = mapHeight
+        }
+        GlideApp.with(holder.staticMapImageView)
+                .load(location)
+                .apply(RequestOptions.centerCropTransform())
+                .listener(object : RequestListener<Drawable> {
+                    override fun onLoadFailed(e: GlideException?,
+                                              model: Any?,
+                                              target: Target<Drawable>?,
+                                              isFirstResource: Boolean): Boolean {
+                        holder.staticMapPinImageView.setImageResource(R.drawable.ic_location_pin_failed)
+                        holder.staticMapErrorTextView.isVisible = true
+                        return false
+                    }
+
+                    override fun onResourceReady(resource: Drawable?,
+                                                 model: Any?,
+                                                 target: Target<Drawable>?,
+                                                 dataSource: DataSource?,
+                                                 isFirstResource: Boolean): Boolean {
+                        locationPinProvider?.create(userId) { pinDrawable ->
+                            // we are not using Glide since it does not display it correctly when there is no user photo
+                            holder.staticMapPinImageView.setImageDrawable(pinDrawable)
+                        }
+                        holder.staticMapErrorTextView.isVisible = false
+                        return false
+                    }
+                })
+                .transform(imageCornerTransformation)
+                .into(holder.staticMapImageView)
+    }
+
+    override fun getViewStubId() = STUB_ID
+
+    open class Holder : AbsMessageItem.Holder(STUB_ID) {
+        val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
+        val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
+        val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView)
+    }
+
+    companion object {
+        private const val STUB_ID = R.id.messageContentLocationStub
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
new file mode 100644
index 0000000000..6163ac5463
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import androidx.core.view.isVisible
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.location.live.LocationLiveMessageBannerView
+import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
+
+@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
+abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocationItem.Holder>() {
+
+    // TODO define the needed attributes
+
+    override fun bind(holder: Holder) {
+        super.bind(holder)
+        bindLocationLiveBanner(holder)
+    }
+
+    private fun bindLocationLiveBanner(holder: Holder) {
+        val messageLayout = attributes.informationData.messageLayout
+        val viewState = if (messageLayout is TimelineMessageLayout.Bubble) {
+            LocationLiveMessageBannerViewState.Emitter(
+                    remainingTimeInMillis = 4000 * 1000L,
+                    bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+                    bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+                    isStopButtonCenteredVertically = false
+            )
+        } else {
+            val dimensionConverter = DimensionConverter(holder.view.resources)
+            val cornerRadius = dimensionConverter.dpToPx(8).toFloat()
+//            LocationLiveMessageBannerViewState.Watcher(
+//                    bottomStartCornerRadiusInDp = cornerRadius,
+//                    bottomEndCornerRadiusInDp = cornerRadius,
+//                    formattedLocalTimeOfEndOfLive = "12:34",
+//            )
+            LocationLiveMessageBannerViewState.Emitter(
+                    remainingTimeInMillis = 4000 * 1000L,
+                    bottomStartCornerRadiusInDp = cornerRadius,
+                    bottomEndCornerRadiusInDp = cornerRadius,
+                    isStopButtonCenteredVertically = true
+            )
+        }
+        holder.locationLiveMessageBanner.isVisible = true
+        holder.locationLiveMessageBanner.render(viewState)
+
+        // TODO adjust Copyright map placement if needed
+    }
+
+    class Holder : AbsMessageLocationItem.Holder() {
+        val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
index cb823aae06..7da9149dc4 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 New Vector Ltd
+ * Copyright (c) 2022 New Vector Ltd
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,137 +16,10 @@
 
 package im.vector.app.features.home.room.detail.timeline.item
 
-import android.graphics.drawable.Drawable
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.core.view.isVisible
-import androidx.core.view.updateLayoutParams
-import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.RequestOptions
-import com.bumptech.glide.request.target.Target
 import im.vector.app.R
-import im.vector.app.core.glide.GlideApp
-import im.vector.app.core.utils.DimensionConverter
-import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
-import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
-import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
-import im.vector.app.features.location.live.LocationLiveMessageBannerView
-import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
-abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>() {
-
-    @EpoxyAttribute
-    var locationUrl: String? = null
-
-    @EpoxyAttribute
-    var userId: String? = null
-
-    @EpoxyAttribute
-    var mapWidth: Int = 0
-
-    @EpoxyAttribute
-    var mapHeight: Int = 0
-
-    @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
-    var locationPinProvider: LocationPinProvider? = null
-
-    override fun bind(holder: Holder) {
-        super.bind(holder)
-        renderSendState(holder.view, null)
-        bindMap(holder)
-        bindLocationLiveBanner(holder)
-    }
-
-    private fun bindMap(holder: Holder) {
-        val location = locationUrl ?: return
-        val messageLayout = attributes.informationData.messageLayout
-        val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
-            messageLayout.cornersRadius.granularRoundedCorners()
-        } else {
-            val dimensionConverter = DimensionConverter(holder.view.resources)
-            RoundedCorners(dimensionConverter.dpToPx(8))
-        }
-        holder.staticMapImageView.updateLayoutParams {
-            width = mapWidth
-            height = mapHeight
-        }
-        GlideApp.with(holder.staticMapImageView)
-                .load(location)
-                .apply(RequestOptions.centerCropTransform())
-                .listener(object : RequestListener<Drawable> {
-                    override fun onLoadFailed(e: GlideException?,
-                                              model: Any?,
-                                              target: Target<Drawable>?,
-                                              isFirstResource: Boolean): Boolean {
-                        holder.staticMapPinImageView.setImageResource(R.drawable.ic_location_pin_failed)
-                        holder.staticMapErrorTextView.isVisible = true
-                        return false
-                    }
-
-                    override fun onResourceReady(resource: Drawable?,
-                                                 model: Any?,
-                                                 target: Target<Drawable>?,
-                                                 dataSource: DataSource?,
-                                                 isFirstResource: Boolean): Boolean {
-                        locationPinProvider?.create(userId) { pinDrawable ->
-                            // we are not using Glide since it does not display it correctly when there is no user photo
-                            holder.staticMapPinImageView.setImageDrawable(pinDrawable)
-                        }
-                        holder.staticMapErrorTextView.isVisible = false
-                        return false
-                    }
-                })
-                .transform(imageCornerTransformation)
-                .into(holder.staticMapImageView)
-    }
-
-    private fun bindLocationLiveBanner(holder: Holder) {
-        val messageLayout = attributes.informationData.messageLayout
-        val viewState = if (messageLayout is TimelineMessageLayout.Bubble) {
-            LocationLiveMessageBannerViewState.Emitter(
-                    remainingTimeInMillis = 4000 * 1000L,
-                    bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
-                    bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
-                    isStopButtonCenteredVertically = false
-            )
-        } else {
-            val dimensionConverter = DimensionConverter(holder.view.resources)
-            val cornerRadius = dimensionConverter.dpToPx(8).toFloat()
-//            LocationLiveMessageBannerViewState.Watcher(
-//                    bottomStartCornerRadiusInDp = cornerRadius,
-//                    bottomEndCornerRadiusInDp = cornerRadius,
-//                    formattedLocalTimeOfEndOfLive = "12:34",
-//            )
-            LocationLiveMessageBannerViewState.Emitter(
-                    remainingTimeInMillis = 4000 * 1000L,
-                    bottomStartCornerRadiusInDp = cornerRadius,
-                    bottomEndCornerRadiusInDp = cornerRadius,
-                    isStopButtonCenteredVertically = true
-            )
-        }
-        holder.locationLiveMessageBanner.isVisible = true
-        holder.locationLiveMessageBanner.render(viewState)
-
-        // TODO create a dedicated message Item per state: Start, Location, End? Check if inheritance is possible in Epoxy model
-        // TODO adjust Copyright map placement
-    }
-
-    override fun getViewStubId() = STUB_ID
-
-    class Holder : AbsMessageItem.Holder(STUB_ID) {
-        val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
-        val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
-        val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView)
-        val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
-    }
-
-    companion object {
-        private const val STUB_ID = R.id.messageContentLocationStub
-    }
+abstract class MessageLocationItem : AbsMessageLocationItem<MessageLocationItem.Holder>() {
+    class Holder : AbsMessageLocationItem.Holder()
 }
diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
index 036d5149b0..656743f26c 100644
--- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
@@ -49,8 +49,10 @@
         android:id="@+id/locationLiveMessageBanner"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        app:layout_constraintStart_toStartOf="@id/staticMapImageView"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="@id/staticMapImageView"
         app:layout_constraintEnd_toEndOf="@id/staticMapImageView"
-        app:layout_constraintBottom_toBottomOf="@id/staticMapImageView" />
+        app:layout_constraintStart_toStartOf="@id/staticMapImageView"
+        tools:visibility="visible" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>

From 93634cd7af37dc24020ff70b7e971b098e4a216f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 11 Apr 2022 15:47:52 +0200
Subject: [PATCH 12/45] Removing TODO

---
 .../app/features/location/live/LocationLiveMessageBannerView.kt  | 1 -
 1 file changed, 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 989adc27b5..bc161c62c6 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -34,7 +34,6 @@ import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
 import im.vector.app.features.themes.ThemeUtils
 import org.threeten.bp.Duration
 
-// TODO should it be moved to timeline.item package?
 sealed class LocationLiveMessageBannerViewState(
         open val bottomStartCornerRadiusInDp: Float,
         open val bottomEndCornerRadiusInDp: Float,

From 11d9579036bbcfe62b160d4c413e4954d3401924 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 12 Apr 2022 10:32:06 +0200
Subject: [PATCH 13/45] Adding currentUserId attribute

---
 .../timeline/item/MessageLiveLocationItem.kt  | 73 +++++++++++++------
 1 file changed, 50 insertions(+), 23 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index 6163ac5463..741866784a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -17,6 +17,7 @@
 package im.vector.app.features.home.room.detail.timeline.item
 
 import androidx.core.view.isVisible
+import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
 import im.vector.app.core.utils.DimensionConverter
@@ -28,6 +29,8 @@ import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
 abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocationItem.Holder>() {
 
     // TODO define the needed attributes
+    @EpoxyAttribute
+    var currentUserId: String? = null
 
     override fun bind(holder: Holder) {
         super.bind(holder)
@@ -35,35 +38,59 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
     }
 
     private fun bindLocationLiveBanner(holder: Holder) {
+        // TODO add check on device id to confirm that is the one that sent the beacon
+        val isEmitter = currentUserId != null && currentUserId == userId
         val messageLayout = attributes.informationData.messageLayout
-        val viewState = if (messageLayout is TimelineMessageLayout.Bubble) {
-            LocationLiveMessageBannerViewState.Emitter(
-                    remainingTimeInMillis = 4000 * 1000L,
-                    bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
-                    bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
-                    isStopButtonCenteredVertically = false
-            )
-        } else {
-            val dimensionConverter = DimensionConverter(holder.view.resources)
-            val cornerRadius = dimensionConverter.dpToPx(8).toFloat()
-//            LocationLiveMessageBannerViewState.Watcher(
-//                    bottomStartCornerRadiusInDp = cornerRadius,
-//                    bottomEndCornerRadiusInDp = cornerRadius,
-//                    formattedLocalTimeOfEndOfLive = "12:34",
-//            )
-            LocationLiveMessageBannerViewState.Emitter(
-                    remainingTimeInMillis = 4000 * 1000L,
-                    bottomStartCornerRadiusInDp = cornerRadius,
-                    bottomEndCornerRadiusInDp = cornerRadius,
-                    isStopButtonCenteredVertically = true
-            )
-        }
+        val viewState = buildViewState(holder, messageLayout, isEmitter)
         holder.locationLiveMessageBanner.isVisible = true
         holder.locationLiveMessageBanner.render(viewState)
-
         // TODO adjust Copyright map placement if needed
     }
 
+    private fun buildViewState(
+            holder: Holder,
+            messageLayout: TimelineMessageLayout,
+            isEmitter: Boolean
+    ): LocationLiveMessageBannerViewState {
+        return when {
+            messageLayout is TimelineMessageLayout.Bubble && isEmitter ->
+                LocationLiveMessageBannerViewState.Emitter(
+                        remainingTimeInMillis = 4000 * 1000L,
+                        bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+                        bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+                        isStopButtonCenteredVertically = false
+                )
+            messageLayout is TimelineMessageLayout.Bubble              ->
+                LocationLiveMessageBannerViewState.Watcher(
+                        bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
+                        bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
+                        formattedLocalTimeOfEndOfLive = "12:34",
+                )
+            isEmitter                                                  -> {
+                val cornerRadius = getBannerCornerRadiusForDefaultLayout(holder)
+                LocationLiveMessageBannerViewState.Emitter(
+                        remainingTimeInMillis = 4000 * 1000L,
+                        bottomStartCornerRadiusInDp = cornerRadius,
+                        bottomEndCornerRadiusInDp = cornerRadius,
+                        isStopButtonCenteredVertically = true
+                )
+            }
+            else                                                       -> {
+                val cornerRadius = getBannerCornerRadiusForDefaultLayout(holder)
+                LocationLiveMessageBannerViewState.Watcher(
+                        bottomStartCornerRadiusInDp = cornerRadius,
+                        bottomEndCornerRadiusInDp = cornerRadius,
+                        formattedLocalTimeOfEndOfLive = "12:34",
+                )
+            }
+        }
+    }
+
+    private fun getBannerCornerRadiusForDefaultLayout(holder: Holder): Float {
+        val dimensionConverter = DimensionConverter(holder.view.resources)
+        return dimensionConverter.dpToPx(8).toFloat()
+    }
+
     class Holder : AbsMessageLocationItem.Holder() {
         val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
     }

From c432985cb45aa4442316763f358be9c5592a695f Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 12 Apr 2022 15:02:24 +0200
Subject: [PATCH 14/45] Adding countDownTimer for emitter view

---
 .../live/LocationLiveMessageBannerView.kt     | 20 +++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index bc161c62c6..3c2a13655b 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -18,6 +18,7 @@ package im.vector.app.features.location.live
 
 import android.content.Context
 import android.graphics.drawable.ColorDrawable
+import android.os.CountDownTimer
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.widget.Button
@@ -53,6 +54,8 @@ sealed class LocationLiveMessageBannerViewState(
     ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
 }
 
+private const val REMAINING_TIME_COUNTER_INTERVAL_IN_MS = 1000L
+
 class LocationLiveMessageBannerView @JvmOverloads constructor(
         context: Context,
         attrs: AttributeSet? = null,
@@ -76,6 +79,8 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
     private val subTitle: TextView
         get() = binding.locationLiveMessageBannerSubTitle
 
+    private var countDownTimer: CountDownTimer? = null
+
     fun render(viewState: LocationLiveMessageBannerViewState) {
         when (viewState) {
             is LocationLiveMessageBannerViewState.Emitter -> renderEmitter(viewState)
@@ -91,8 +96,19 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
     private fun renderEmitter(viewState: LocationLiveMessageBannerViewState.Emitter) {
         stopButton.isVisible = true
         title.text = context.getString(R.string.location_share_live_enabled)
-        val duration = Duration.ofMillis(viewState.remainingTimeInMillis.coerceAtLeast(0L))
-        subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+
+        countDownTimer?.cancel()
+        countDownTimer = object : CountDownTimer(viewState.remainingTimeInMillis, REMAINING_TIME_COUNTER_INTERVAL_IN_MS) {
+            override fun onTick(millisUntilFinished: Long) {
+                val duration = Duration.ofMillis(millisUntilFinished.coerceAtLeast(0L))
+                subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+            }
+
+            override fun onFinish() {
+                subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L)))
+            }
+        }
+        countDownTimer?.start()
 
         val rootLayout: ConstraintLayout? = (binding.root as? ConstraintLayout)
         rootLayout?.let { parentLayout ->

From 8b628229ed4ab111959bba9c9af2cbc29178a783 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 12 Apr 2022 15:21:30 +0200
Subject: [PATCH 15/45] Do not append 0 values when formatting duration

---
 .../java/im/vector/app/core/utils/TextUtils.kt | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index bb197b2e9d..f9c33ffe2a 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -93,15 +93,21 @@ object TextUtils {
         when {
             hours > 0   -> {
                 appendHours(context, builder, hours)
-                builder.append(" ")
-                appendMinutes(context, builder, minutes)
-                builder.append(" ")
-                appendSeconds(context, builder, seconds)
+                if(minutes > 0) {
+                    builder.append(" ")
+                    appendMinutes(context, builder, minutes)
+                }
+                if(seconds > 0) {
+                    builder.append(" ")
+                    appendSeconds(context, builder, seconds)
+                }
             }
             minutes > 0 -> {
                 appendMinutes(context, builder, minutes)
-                builder.append(" ")
-                appendSeconds(context, builder, seconds)
+                if(seconds > 0) {
+                    builder.append(" ")
+                    appendSeconds(context, builder, seconds)
+                }
             }
             else        -> {
                 appendSeconds(context, builder, seconds)

From adbc430ac8d2e8d619ee296d2b086307ec5e8ecb Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 10:29:14 +0200
Subject: [PATCH 16/45] Renaming userId into locationUserId in message location
 item

---
 .../home/room/detail/timeline/factory/MessageItemFactory.kt   | 4 ++--
 .../home/room/detail/timeline/item/AbsMessageLocationItem.kt  | 4 ++--
 .../home/room/detail/timeline/item/MessageLiveLocationItem.kt | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 6f22b3eff4..868018a706 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -237,14 +237,14 @@ class MessageItemFactory @Inject constructor(
             urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
         }
 
-        val userId = if (locationContent.isSelfLocation()) informationData.senderId else null
+        val locationUserId = if (locationContent.isSelfLocation()) informationData.senderId else null
 
         return MessageLocationItem_()
                 .attributes(attributes)
                 .locationUrl(locationUrl)
                 .mapWidth(width)
                 .mapHeight(height)
-                .userId(userId)
+                .locationUserId(locationUserId)
                 .locationPinProvider(locationPinProvider)
                 .highlighted(highlight)
                 .leftGuideline(avatarSizeProvider.leftGuideline)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
index b95fd0f4e1..ad18759fc6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
@@ -41,7 +41,7 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder> : AbsMe
     var locationUrl: String? = null
 
     @EpoxyAttribute
-    var userId: String? = null
+    var locationUserId: String? = null
 
     @EpoxyAttribute
     var mapWidth: Int = 0
@@ -89,7 +89,7 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder> : AbsMe
                                                  target: Target<Drawable>?,
                                                  dataSource: DataSource?,
                                                  isFirstResource: Boolean): Boolean {
-                        locationPinProvider?.create(userId) { pinDrawable ->
+                        locationPinProvider?.create(locationUserId) { pinDrawable ->
                             // we are not using Glide since it does not display it correctly when there is no user photo
                             holder.staticMapPinImageView.setImageDrawable(pinDrawable)
                         }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index 741866784a..bbf2ca1348 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -39,7 +39,7 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
 
     private fun bindLocationLiveBanner(holder: Holder) {
         // TODO add check on device id to confirm that is the one that sent the beacon
-        val isEmitter = currentUserId != null && currentUserId == userId
+        val isEmitter = currentUserId != null && currentUserId == locationUserId
         val messageLayout = attributes.informationData.messageLayout
         val viewState = buildViewState(holder, messageLayout, isEmitter)
         holder.locationLiveMessageBanner.isVisible = true

From 077977b8bf4e5423114a603b5e1f779deb42d1af Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 12:18:21 +0200
Subject: [PATCH 17/45] Show running live state item

---
 .../LiveLocationShareMessageItemFactory.kt    | 43 +++++++++++++++++--
 .../timeline/item/MessageLiveLocationItem.kt  | 30 ++++++++++---
 .../app/features/location/LocationData.kt     | 11 ++++-
 .../live/LocationLiveMessageBannerView.kt     | 24 ++++++-----
 4 files changed, 88 insertions(+), 20 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index 760ec92cd6..5d9742e1c6 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -16,24 +16,36 @@
 
 package im.vector.app.features.home.room.detail.timeline.factory
 
+import im.vector.app.core.date.VectorDateFormatter
 import im.vector.app.core.epoxy.VectorEpoxyModel
 import im.vector.app.core.resources.DateProvider
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
+import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
 import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem_
+import im.vector.app.features.location.INITIAL_MAP_ZOOM_IN_TIMELINE
+import im.vector.app.features.location.UrlMapProvider
+import im.vector.app.features.location.toLocationData
 import org.matrix.android.sdk.api.extensions.orFalse
+import org.matrix.android.sdk.api.session.Session
 import org.threeten.bp.LocalDateTime
 import timber.log.Timber
 import javax.inject.Inject
 
 class LiveLocationShareMessageItemFactory @Inject constructor(
+        private val session: Session,
         private val dimensionConverter: DimensionConverter,
         private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
         private val avatarSizeProvider: AvatarSizeProvider,
+        private val urlMapProvider: UrlMapProvider,
+        private val locationPinProvider: LocationPinProvider,
+        private val vectorDateFormatter: VectorDateFormatter,
 ) {
 
     fun create(
@@ -41,10 +53,10 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
-        return when (getViewState(liveLocationShareSummaryData)) {
+        return when (val currentState = getViewState(liveLocationShareSummaryData)) {
             LiveLocationShareViewState.Loading    -> buildLoadingItem(highlight, attributes)
             LiveLocationShareViewState.Inactive   -> buildInactiveItem()
-            is LiveLocationShareViewState.Running -> buildRunningItem()
+            is LiveLocationShareViewState.Running -> buildRunningItem(highlight, attributes, currentState)
             LiveLocationShareViewState.Unkwown    -> null
         }
     }
@@ -64,7 +76,32 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
                 .leftGuideline(avatarSizeProvider.leftGuideline)
     }
 
-    private fun buildRunningItem() = null
+    private fun buildRunningItem(
+            highlight: Boolean,
+            attributes: AbsMessageItem.Attributes,
+            runningState: LiveLocationShareViewState.Running,
+    ): MessageLiveLocationItem {
+        // TODO only render location if enabled in preferences: to be handled in a next PR
+        val width = timelineMediaSizeProvider.getMaxSize().first
+        val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
+
+        val locationUrl = runningState.lastGeoUri.toLocationData()?.let {
+            urlMapProvider.buildStaticMapUrl(it, INITIAL_MAP_ZOOM_IN_TIMELINE, width, height)
+        }
+
+        return MessageLiveLocationItem_()
+                .attributes(attributes)
+                .locationUrl(locationUrl)
+                .mapWidth(width)
+                .mapHeight(height)
+                .locationUserId(attributes.informationData.senderId)
+                .locationPinProvider(locationPinProvider)
+                .highlighted(highlight)
+                .leftGuideline(avatarSizeProvider.leftGuideline)
+                .currentUserId(session.myUserId)
+                .endOfLiveDateTime(runningState.endOfLiveDateTime)
+                .vectorDateFormatter(vectorDateFormatter)
+    }
 
     private fun buildInactiveItem() = null
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index bbf2ca1348..d81e26f29b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -20,30 +20,42 @@ import androidx.core.view.isVisible
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
 import im.vector.app.R
+import im.vector.app.core.date.DateFormatKind
+import im.vector.app.core.date.VectorDateFormatter
+import im.vector.app.core.resources.toTimestamp
 import im.vector.app.core.utils.DimensionConverter
 import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
 import im.vector.app.features.location.live.LocationLiveMessageBannerView
 import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
+import org.threeten.bp.LocalDateTime
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
 abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocationItem.Holder>() {
 
-    // TODO define the needed attributes
     @EpoxyAttribute
     var currentUserId: String? = null
 
+    @EpoxyAttribute
+    var endOfLiveDateTime: LocalDateTime? = null
+
+    @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
+    lateinit var vectorDateFormatter: VectorDateFormatter
+
     override fun bind(holder: Holder) {
         super.bind(holder)
         bindLocationLiveBanner(holder)
     }
 
     private fun bindLocationLiveBanner(holder: Holder) {
-        // TODO add check on device id to confirm that is the one that sent the beacon
+        // TODO in a future PR add check on device id to confirm that is the one that sent the beacon
         val isEmitter = currentUserId != null && currentUserId == locationUserId
         val messageLayout = attributes.informationData.messageLayout
         val viewState = buildViewState(holder, messageLayout, isEmitter)
         holder.locationLiveMessageBanner.isVisible = true
         holder.locationLiveMessageBanner.render(viewState)
+        holder.locationLiveMessageBanner.stopButton.setOnClickListener {
+            // TODO call stop live location
+        }
         // TODO adjust Copyright map placement if needed
     }
 
@@ -55,7 +67,7 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
         return when {
             messageLayout is TimelineMessageLayout.Bubble && isEmitter ->
                 LocationLiveMessageBannerViewState.Emitter(
-                        remainingTimeInMillis = 4000 * 1000L,
+                        remainingTimeInMillis = getRemainingTimeOfLiveInMillis(),
                         bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
                         bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
                         isStopButtonCenteredVertically = false
@@ -64,12 +76,12 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
                 LocationLiveMessageBannerViewState.Watcher(
                         bottomStartCornerRadiusInDp = messageLayout.cornersRadius.bottomStartRadius,
                         bottomEndCornerRadiusInDp = messageLayout.cornersRadius.bottomEndRadius,
-                        formattedLocalTimeOfEndOfLive = "12:34",
+                        formattedLocalTimeOfEndOfLive = getFormattedLocalTimeEndOfLive(),
                 )
             isEmitter                                                  -> {
                 val cornerRadius = getBannerCornerRadiusForDefaultLayout(holder)
                 LocationLiveMessageBannerViewState.Emitter(
-                        remainingTimeInMillis = 4000 * 1000L,
+                        remainingTimeInMillis = getRemainingTimeOfLiveInMillis(),
                         bottomStartCornerRadiusInDp = cornerRadius,
                         bottomEndCornerRadiusInDp = cornerRadius,
                         isStopButtonCenteredVertically = true
@@ -80,7 +92,7 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
                 LocationLiveMessageBannerViewState.Watcher(
                         bottomStartCornerRadiusInDp = cornerRadius,
                         bottomEndCornerRadiusInDp = cornerRadius,
-                        formattedLocalTimeOfEndOfLive = "12:34",
+                        formattedLocalTimeOfEndOfLive = getFormattedLocalTimeEndOfLive(),
                 )
             }
         }
@@ -91,6 +103,12 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
         return dimensionConverter.dpToPx(8).toFloat()
     }
 
+    private fun getFormattedLocalTimeEndOfLive() =
+            endOfLiveDateTime?.toTimestamp()?.let { vectorDateFormatter.format(it, DateFormatKind.MESSAGE_SIMPLE) }.orEmpty()
+
+    private fun getRemainingTimeOfLiveInMillis() =
+            (endOfLiveDateTime?.toTimestamp() ?: 0) - LocalDateTime.now().toTimestamp()
+
     class Holder : AbsMessageLocationItem.Holder() {
         val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
     }
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
index a69d8d20e3..96d544ba5a 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
@@ -29,7 +29,7 @@ data class LocationData(
 ) : Parcelable
 
 /**
- * Creates location data from a LocationContent
+ * Creates location data from a MessageLocationContent
  * "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
  * @return location data or null if geo uri is not valid
  */
@@ -37,6 +37,15 @@ fun MessageLocationContent.toLocationData(): LocationData? {
     return parseGeo(getBestGeoUri())
 }
 
+/**
+ * Creates location data from a geoUri String
+ * "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
+ * @return location data or null if geo uri is null or not valid
+ */
+fun String?.toLocationData(): LocationData? {
+    return this?.let { parseGeo(it) }
+}
+
 @VisibleForTesting
 fun parseGeo(geo: String): LocationData? {
     val geoParts = geo
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 3c2a13655b..7d008e0fc6 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -98,17 +98,21 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
         title.text = context.getString(R.string.location_share_live_enabled)
 
         countDownTimer?.cancel()
-        countDownTimer = object : CountDownTimer(viewState.remainingTimeInMillis, REMAINING_TIME_COUNTER_INTERVAL_IN_MS) {
-            override fun onTick(millisUntilFinished: Long) {
-                val duration = Duration.ofMillis(millisUntilFinished.coerceAtLeast(0L))
-                subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
-            }
+        viewState.remainingTimeInMillis
+                .takeIf { it >= 0 }
+                ?.let {
+                    countDownTimer = object : CountDownTimer(it, REMAINING_TIME_COUNTER_INTERVAL_IN_MS) {
+                        override fun onTick(millisUntilFinished: Long) {
+                            val duration = Duration.ofMillis(millisUntilFinished.coerceAtLeast(0L))
+                            subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+                        }
 
-            override fun onFinish() {
-                subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L)))
-            }
-        }
-        countDownTimer?.start()
+                        override fun onFinish() {
+                            subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L)))
+                        }
+                    }
+                    countDownTimer?.start()
+                }
 
         val rootLayout: ConstraintLayout? = (binding.root as? ConstraintLayout)
         rootLayout?.let { parentLayout ->

From b577f6ab8eec494d4cf23c696360319c81bd3cd2 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 14:42:24 +0200
Subject: [PATCH 18/45] Fix display in Bubble mode

---
 .../android/sdk/api/session/room/timeline/TimelineEvent.kt   | 2 ++
 .../timeline/factory/LiveLocationShareMessageItemFactory.kt  | 5 ++++-
 .../detail/timeline/style/TimelineMessageLayoutFactory.kt    | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
index adbc8ab12a..6793ac62d3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/timeline/TimelineEvent.kt
@@ -30,6 +30,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.EventAnnotationsSummary
 import org.matrix.android.sdk.api.session.room.model.ReadReceipt
 import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageContent
 import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@@ -140,6 +141,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
         EventType.STICKER                   -> root.getClearContent().toModel<MessageStickerContent>()
         in EventType.POLL_START             -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessagePollContent>()
         in EventType.STATE_ROOM_BEACON_INFO -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageBeaconInfoContent>()
+        in EventType.BEACON_LOCATION_DATA   -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel<MessageBeaconLocationDataContent>()
         else                                -> (annotations?.editSummary?.latestContent ?: root.getClearContent()).toModel()
     }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index 5d9742e1c6..8217d1cc33 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -53,12 +53,15 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
-        return when (val currentState = getViewState(liveLocationShareSummaryData)) {
+        val item = when (val currentState = getViewState(liveLocationShareSummaryData)) {
             LiveLocationShareViewState.Loading    -> buildLoadingItem(highlight, attributes)
             LiveLocationShareViewState.Inactive   -> buildInactiveItem()
             is LiveLocationShareViewState.Running -> buildRunningItem(highlight, attributes, currentState)
             LiveLocationShareViewState.Unkwown    -> null
         }
+        item?.layout(attributes.informationData.messageLayout.layoutRes)
+
+        return item
     }
 
     private fun buildLoadingItem(
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
index f083c70100..fab7dcb49a 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
@@ -146,7 +146,7 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
 
     private fun MessageContent?.timestampInsideMessage(): Boolean {
         if (this == null) return false
-        if (msgType == MessageType.MSGTYPE_LOCATION) return vectorPreferences.labsRenderLocationsInTimeline()
+        if (msgType == MessageType.MSGTYPE_LOCATION || msgType == MessageType.MSGTYPE_BEACON_LOCATION_DATA) return vectorPreferences.labsRenderLocationsInTimeline()
         return this.msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
     }
 

From 3a36762632738785a4244896e7cdef64be36115e Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 14:56:36 +0200
Subject: [PATCH 19/45] Fix some namings after rebase

---
 .../timeline/factory/LiveLocationShareMessageItemFactory.kt     | 2 +-
 .../detail/timeline/helper/MessageInformationDataFactory.kt     | 2 +-
 .../home/room/detail/timeline/item/MessageInformationData.kt    | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index 8217d1cc33..cf8423593d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -132,7 +132,7 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
     }
 
     private fun getEndOfLiveDateTime(liveLocationShareSummaryData: LiveLocationShareSummaryData): LocalDateTime? {
-        return liveLocationShareSummaryData.endOfLiveTimestampAsMilliseconds?.let { DateProvider.toLocalDateTime(timestamp = it) }
+        return liveLocationShareSummaryData.endOfLiveTimestampMillis?.let { DateProvider.toLocalDateTime(timestamp = it) }
     }
 
     private sealed class LiveLocationShareViewState {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
index 8fb7aea7e4..4425415563 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
@@ -193,7 +193,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
         return event.annotations?.liveLocationShareAggregatedSummary?.let { summary ->
             LiveLocationShareSummaryData(
                     isActive = summary.isActive,
-                    endOfLiveTimestampAsMilliseconds = summary.endOfLiveTimestampAsMilliseconds,
+                    endOfLiveTimestampMillis = summary.endOfLiveTimestampMillis,
                     lastGeoUri = summary.lastLocationDataContent?.getBestLocationInfo()?.geoUri
             )
         }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
index 8ad4034a32..dae1c3671d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
@@ -104,7 +104,7 @@ data class PollVoteSummaryData(
 @Parcelize
 data class LiveLocationShareSummaryData(
         val isActive: Boolean?,
-        val endOfLiveTimestampAsMilliseconds: Long?,
+        val endOfLiveTimestampMillis: Long?,
         val lastGeoUri: String?,
 ) : Parcelable
 

From d341611e544afdcd569314daff7d0cf2c39abfad Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 15:00:17 +0200
Subject: [PATCH 20/45] Format location event for debugging purpose

---
 .../detail/timeline/factory/TimelineItemFactory.kt  | 13 +++++++------
 .../detail/timeline/format/NoticeEventFormatter.kt  |  3 ++-
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
index f4bcc1ba65..9b6026031e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
@@ -100,7 +100,7 @@ class TimelineItemFactory @Inject constructor(
                     // Message itemsX
                     EventType.STICKER,
                     in EventType.POLL_START,
-                    EventType.MESSAGE               -> messageItemFactory.create(params)
+                    EventType.MESSAGE                 -> messageItemFactory.create(params)
                     EventType.REDACTION,
                     EventType.KEY_VERIFICATION_ACCEPT,
                     EventType.KEY_VERIFICATION_START,
@@ -113,14 +113,15 @@ class TimelineItemFactory @Inject constructor(
                     EventType.CALL_NEGOTIATE,
                     EventType.REACTION,
                     in EventType.POLL_RESPONSE,
-                    in EventType.POLL_END           -> noticeItemFactory.create(params)
+                    in EventType.POLL_END             -> noticeItemFactory.create(params)
                     // Calls
                     EventType.CALL_INVITE,
                     EventType.CALL_HANGUP,
                     EventType.CALL_REJECT,
-                    EventType.CALL_ANSWER           -> callItemFactory.create(params)
+                    EventType.CALL_ANSWER             -> callItemFactory.create(params)
+                    in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
                     // Crypto
-                    EventType.ENCRYPTED             -> {
+                    EventType.ENCRYPTED               -> {
                         if (event.root.isRedacted()) {
                             // Redacted event, let the MessageItemFactory handle it
                             messageItemFactory.create(params)
@@ -129,11 +130,11 @@ class TimelineItemFactory @Inject constructor(
                         }
                     }
                     EventType.KEY_VERIFICATION_CANCEL,
-                    EventType.KEY_VERIFICATION_DONE -> {
+                    EventType.KEY_VERIFICATION_DONE   -> {
                         verificationConclusionItemFactory.create(params)
                     }
                     // Unhandled event types
-                    else                            -> {
+                    else                              -> {
                         // Should only happen when shouldShowHiddenEvents() settings is ON
                         Timber.v("Type ${event.root.getClearType()} not handled")
                         defaultItemFactory.create(params)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
index 7ad0cb27c6..8e06b3ee5d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/NoticeEventFormatter.kt
@@ -107,7 +107,8 @@ class NoticeEventFormatter @Inject constructor(
             EventType.REDACTION,
             EventType.STICKER,
             in EventType.POLL_RESPONSE,
-            in EventType.POLL_END                   -> formatDebug(timelineEvent.root)
+            in EventType.POLL_END,
+            in EventType.BEACON_LOCATION_DATA       -> formatDebug(timelineEvent.root)
             else                                    -> {
                 Timber.v("Type $type not handled by this formatter")
                 null

From 4a4ec47c4901a56f041e138341342c8e27cf79ac Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 15:16:03 +0200
Subject: [PATCH 21/45] Binding stop button of emitter view

---
 .../detail/timeline/helper/MessageItemAttributesFactory.kt     | 1 +
 .../features/home/room/detail/timeline/item/AbsMessageItem.kt  | 1 +
 .../home/room/detail/timeline/item/MessageLiveLocationItem.kt  | 3 ++-
 3 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
index 45c711ff93..737b0dc85d 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageItemAttributesFactory.kt
@@ -57,6 +57,7 @@ class MessageItemAttributesFactory @Inject constructor(
                 memberClickListener = {
                     callback?.onMemberNameClicked(informationData)
                 },
+                callback = callback,
                 reactionPillCallback = callback,
                 avatarCallback = callback,
                 threadCallback = callback,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
index 30c366738d..d52b254ff4 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageItem.kt
@@ -178,6 +178,7 @@ abstract class AbsMessageItem<H : AbsMessageItem.Holder> : AbsBaseMessageItem<H>
             override val itemLongClickListener: View.OnLongClickListener? = null,
             override val itemClickListener: ClickListener? = null,
             val memberClickListener: ClickListener? = null,
+            val callback: TimelineEventController.Callback? = null,
             override val reactionPillCallback: TimelineEventController.ReactionPillCallback? = null,
             val avatarCallback: TimelineEventController.AvatarCallback? = null,
             val threadCallback: TimelineEventController.ThreadCallback? = null,
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index d81e26f29b..d68e091564 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -24,6 +24,7 @@ import im.vector.app.core.date.DateFormatKind
 import im.vector.app.core.date.VectorDateFormatter
 import im.vector.app.core.resources.toTimestamp
 import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.RoomDetailAction
 import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
 import im.vector.app.features.location.live.LocationLiveMessageBannerView
 import im.vector.app.features.location.live.LocationLiveMessageBannerViewState
@@ -54,7 +55,7 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
         holder.locationLiveMessageBanner.isVisible = true
         holder.locationLiveMessageBanner.render(viewState)
         holder.locationLiveMessageBanner.stopButton.setOnClickListener {
-            // TODO call stop live location
+            attributes.callback?.onTimelineItemAction(RoomDetailAction.StopLiveLocationSharing)
         }
         // TODO adjust Copyright map placement if needed
     }

From b0c1ca87df895afa87e9b621bcb47a27f672d0e3 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 15:16:42 +0200
Subject: [PATCH 22/45] Reducing period of location update to 2 seconds

---
 vector/src/main/java/im/vector/app/features/location/Config.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/Config.kt b/vector/src/main/java/im/vector/app/features/location/Config.kt
index 6f947290e2..c29e2e911a 100644
--- a/vector/src/main/java/im/vector/app/features/location/Config.kt
+++ b/vector/src/main/java/im/vector/app/features/location/Config.kt
@@ -22,5 +22,5 @@ const val DEFAULT_PIN_ID = "DEFAULT_PIN_ID"
 
 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 = 2 * 1_000L // every 2 seconds
 const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f

From 370b6a81bcc9c2def40b5232598cc99914775134 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 15:30:14 +0200
Subject: [PATCH 23/45] Checking inactive state before loading to handle timed
 out live without location

---
 .../timeline/factory/LiveLocationShareMessageItemFactory.kt   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index cf8423593d..753e5116f0 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -110,9 +110,9 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
 
     private fun getViewState(liveLocationShareSummaryData: LiveLocationShareSummaryData?): LiveLocationShareViewState {
         return when {
-            liveLocationShareSummaryData?.isActive == null                                                   -> LiveLocationShareViewState.Unkwown
-            liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
+            liveLocationShareSummaryData?.isActive == null -> LiveLocationShareViewState.Unkwown
             liveLocationShareSummaryData.isActive.not() || isLiveTimedOut(liveLocationShareSummaryData)      -> LiveLocationShareViewState.Inactive
+            liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
             else                                                                                             ->
                 LiveLocationShareViewState.Running(
                         liveLocationShareSummaryData.lastGeoUri.orEmpty(),

From a37edb591b20f8cd0a637bbf9c4cc96299a97980 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 2 May 2022 17:10:27 +0200
Subject: [PATCH 24/45] Adding xml layout for inactive state

---
 ...line_event_live_location_inactive_stub.xml | 74 +++++++++++++++++++
 ...imeline_event_live_location_start_stub.xml |  2 +-
 vector/src/main/res/values/strings.xml        |  1 +
 3 files changed, 76 insertions(+), 1 deletion(-)
 create mode 100644 vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml

diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
new file mode 100644
index 0000000000..e26713b484
--- /dev/null
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <!-- Size will be overrode -->
+    <ImageView
+        android:id="@+id/locationLiveInactiveMap"
+        android:layout_width="300dp"
+        android:layout_height="200dp"
+        android:contentDescription="@string/a11y_static_map_image"
+        android:src="@drawable/bg_no_location_map"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/locationLiveInactiveBanner"
+        android:layout_width="0dp"
+        android:layout_height="48dp"
+        android:alpha="0.85"
+        android:src="@color/element_background_light"
+        app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveMap"
+        app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
+        app:layout_constraintStart_toStartOf="@id/locationLiveInactiveMap"
+        tools:ignore="ContentDescription" />
+
+    <ImageView
+        android:id="@+id/locationLiveInactiveIcon"
+        android:layout_width="0dp"
+        android:layout_height="65dp"
+        android:alpha="0.85"
+        android:src="@drawable/ic_attachment_location_white"
+        app:layout_constraintBottom_toTopOf="@id/locationLiveInactiveVerticalCenter"
+        app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
+        app:layout_constraintStart_toStartOf="@id/locationLiveInactiveMap"
+        app:tint="?vctr_content_quaternary"
+        tools:ignore="ContentDescription" />
+
+    <ImageView
+        android:id="@+id/locationLiveInactiveBannerIcon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:layout_marginHorizontal="8dp"
+        android:background="@drawable/circle"
+        android:backgroundTint="?vctr_content_quaternary"
+        android:padding="3dp"
+        app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveBanner"
+        app:layout_constraintStart_toStartOf="@id/locationLiveInactiveBanner"
+        app:layout_constraintTop_toTopOf="@id/locationLiveInactiveBanner"
+        app:srcCompat="@drawable/ic_attachment_location_live_white"
+        tools:ignore="ContentDescription" />
+
+    <TextView
+        android:id="@+id/locationLiveInactiveTitle"
+        style="@style/Widget.Vector.TextView.Caption"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="8dp"
+        android:text="@string/location_share_live_ended"
+        android:textColor="?vctr_content_tertiary"
+        app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveBanner"
+        app:layout_constraintStart_toEndOf="@id/locationLiveInactiveBannerIcon"
+        app:layout_constraintTop_toTopOf="@id/locationLiveInactiveBanner" />
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/locationLiveInactiveVerticalCenter"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.5" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index b81a6cc0e9..199d36cfde 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -20,7 +20,7 @@
         android:layout_width="0dp"
         android:layout_height="48dp"
         android:alpha="0.85"
-        android:src="?colorSurface"
+        android:src="@color/element_background_light"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveStartMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveStartMap"
         app:layout_constraintStart_toStartOf="@id/locationLiveStartMap"
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 00b29e83b3..7adc751f8e 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -3018,6 +3018,7 @@
     <string name="location_timeline_failed_to_load_map">Failed to load map</string>
     <string name="location_share_live_enabled">Live location enabled</string>
     <string name="location_share_live_started">Loading live location…</string>
+    <string name="location_share_live_ended">Live location ended</string>
     <string name="location_share_live_view">View live location</string>
     <string name="location_share_live_until">Live until %1$s</string>
     <string name="location_share_live_stop">Stop</string>

From c10b2a405c99715013590abac1af09d9bd0a6254 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 3 May 2022 09:54:20 +0200
Subject: [PATCH 25/45] Show inactive message item when a live is inactive

---
 .../LiveLocationShareMessageItemFactory.kt    | 23 +++++-
 .../DefaultLiveLocationShareStatusItem.kt     | 80 +++++++++++++++++++
 .../item/LiveLocationShareStatusItem.kt       | 31 +++++++
 .../item/MessageLiveLocationInactiveItem.kt   | 52 ++++++++++++
 .../item/MessageLiveLocationStartItem.kt      | 53 ++----------
 ...line_event_live_location_inactive_stub.xml | 10 +--
 ...imeline_event_live_location_start_stub.xml |  9 ++-
 ...em_timeline_event_view_stubs_container.xml |  6 ++
 8 files changed, 203 insertions(+), 61 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt
 create mode 100644 vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationInactiveItem.kt

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index 753e5116f0..f993676958 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -25,6 +25,8 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
 import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationInactiveItem
+import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationInactiveItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationStartItem
@@ -54,8 +56,8 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
         val item = when (val currentState = getViewState(liveLocationShareSummaryData)) {
+            LiveLocationShareViewState.Inactive   -> buildInactiveItem(highlight, attributes)
             LiveLocationShareViewState.Loading    -> buildLoadingItem(highlight, attributes)
-            LiveLocationShareViewState.Inactive   -> buildInactiveItem()
             is LiveLocationShareViewState.Running -> buildRunningItem(highlight, attributes, currentState)
             LiveLocationShareViewState.Unkwown    -> null
         }
@@ -64,6 +66,21 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
         return item
     }
 
+    private fun buildInactiveItem(
+            highlight: Boolean,
+            attributes: AbsMessageItem.Attributes,
+    ): MessageLiveLocationInactiveItem {
+        val width = timelineMediaSizeProvider.getMaxSize().first
+        val height = dimensionConverter.dpToPx(MessageItemFactory.MESSAGE_LOCATION_ITEM_HEIGHT_IN_DP)
+
+        return MessageLiveLocationInactiveItem_()
+                .attributes(attributes)
+                .mapWidth(width)
+                .mapHeight(height)
+                .highlighted(highlight)
+                .leftGuideline(avatarSizeProvider.leftGuideline)
+    }
+
     private fun buildLoadingItem(
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
@@ -106,11 +123,9 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
                 .vectorDateFormatter(vectorDateFormatter)
     }
 
-    private fun buildInactiveItem() = null
-
     private fun getViewState(liveLocationShareSummaryData: LiveLocationShareSummaryData?): LiveLocationShareViewState {
         return when {
-            liveLocationShareSummaryData?.isActive == null -> LiveLocationShareViewState.Unkwown
+            liveLocationShareSummaryData?.isActive == null                                                   -> LiveLocationShareViewState.Unkwown
             liveLocationShareSummaryData.isActive.not() || isLiveTimedOut(liveLocationShareSummaryData)      -> LiveLocationShareViewState.Inactive
             liveLocationShareSummaryData.isActive && liveLocationShareSummaryData.lastGeoUri.isNullOrEmpty() -> LiveLocationShareViewState.Loading
             else                                                                                             ->
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
new file mode 100644
index 0000000000..e453b01692
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.content.res.Resources
+import android.graphics.drawable.ColorDrawable
+import android.widget.ImageView
+import androidx.core.view.updateLayoutParams
+import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
+import com.bumptech.glide.load.resource.bitmap.RoundedCorners
+import im.vector.app.R
+import im.vector.app.core.glide.GlideApp
+import im.vector.app.core.utils.DimensionConverter
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
+import im.vector.app.features.themes.ThemeUtils
+
+/**
+ * Default implementation of common methods for item representing the status of a live location share.
+ */
+class DefaultLiveLocationShareStatusItem : LiveLocationShareStatusItem {
+
+    override fun bindMap(
+            mapImageView: ImageView,
+            mapWidth: Int,
+            mapHeight: Int,
+            messageLayout: TimelineMessageLayout
+    ) {
+        val mapCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+            messageLayout.cornersRadius.granularRoundedCorners()
+        } else {
+            RoundedCorners(getDefaultLayoutCornerRadiusInDp(mapImageView.resources))
+        }
+        mapImageView.updateLayoutParams {
+            width = mapWidth
+            height = mapHeight
+        }
+        GlideApp.with(mapImageView)
+                .load(R.drawable.bg_no_location_map)
+                .transform(mapCornerTransformation)
+                .into(mapImageView)
+    }
+
+    override fun bindBottomBanner(bannerImageView: ImageView, messageLayout: TimelineMessageLayout) {
+        val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
+            GranularRoundedCorners(
+                    0f,
+                    0f,
+                    messageLayout.cornersRadius.bottomEndRadius,
+                    messageLayout.cornersRadius.bottomStartRadius
+            )
+        } else {
+            val bottomCornerRadius = getDefaultLayoutCornerRadiusInDp(bannerImageView.resources).toFloat()
+            GranularRoundedCorners(0f, 0f, bottomCornerRadius, bottomCornerRadius)
+        }
+        GlideApp.with(bannerImageView)
+                .load(ColorDrawable(ThemeUtils.getColor(bannerImageView.context, R.attr.colorSurface)))
+                .transform(imageCornerTransformation)
+                .into(bannerImageView)
+    }
+
+    private fun getDefaultLayoutCornerRadiusInDp(resources: Resources): Int {
+        val dimensionConverter = DimensionConverter(resources)
+        return dimensionConverter.dpToPx(8)
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt
new file mode 100644
index 0000000000..2f79f2fc9e
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/LiveLocationShareStatusItem.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.widget.ImageView
+import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
+
+interface LiveLocationShareStatusItem {
+    fun bindMap(
+            mapImageView: ImageView,
+            mapWidth: Int,
+            mapHeight: Int,
+            messageLayout: TimelineMessageLayout
+    )
+
+    fun bindBottomBanner(bannerImageView: ImageView, messageLayout: TimelineMessageLayout)
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationInactiveItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationInactiveItem.kt
new file mode 100644
index 0000000000..bb85316bf1
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationInactiveItem.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.home.room.detail.timeline.item
+
+import android.widget.ImageView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.app.R
+
+@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
+abstract class MessageLiveLocationInactiveItem :
+        AbsMessageItem<MessageLiveLocationInactiveItem.Holder>(),
+        LiveLocationShareStatusItem by DefaultLiveLocationShareStatusItem() {
+
+    @EpoxyAttribute
+    var mapWidth: Int = 0
+
+    @EpoxyAttribute
+    var mapHeight: Int = 0
+
+    override fun bind(holder: Holder) {
+        super.bind(holder)
+        renderSendState(holder.view, null)
+        bindMap(holder.noLocationMapImageView, mapWidth, mapHeight, attributes.informationData.messageLayout)
+        bindBottomBanner(holder.bannerImageView, attributes.informationData.messageLayout)
+    }
+
+    override fun getViewStubId() = STUB_ID
+
+    class Holder : AbsMessageItem.Holder(STUB_ID) {
+        val bannerImageView by bind<ImageView>(R.id.locationLiveInactiveBanner)
+        val noLocationMapImageView by bind<ImageView>(R.id.locationLiveInactiveMap)
+    }
+
+    companion object {
+        private const val STUB_ID = R.id.messageContentLiveLocationInactiveStub
+    }
+}
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
index 390db0ef50..001774b579 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationStartItem.kt
@@ -16,22 +16,15 @@
 
 package im.vector.app.features.home.room.detail.timeline.item
 
-import android.graphics.drawable.ColorDrawable
 import android.widget.ImageView
-import androidx.core.view.updateLayoutParams
 import com.airbnb.epoxy.EpoxyAttribute
 import com.airbnb.epoxy.EpoxyModelClass
-import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners
 import im.vector.app.R
-import im.vector.app.core.glide.GlideApp
-import im.vector.app.core.utils.DimensionConverter
-import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
-import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
-import im.vector.app.features.themes.ThemeUtils
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
-abstract class MessageLiveLocationStartItem : AbsMessageItem<MessageLiveLocationStartItem.Holder>() {
+abstract class MessageLiveLocationStartItem :
+        AbsMessageItem<MessageLiveLocationStartItem.Holder>(),
+        LiveLocationShareStatusItem by DefaultLiveLocationShareStatusItem() {
 
     @EpoxyAttribute
     var mapWidth: Int = 0
@@ -42,44 +35,8 @@ abstract class MessageLiveLocationStartItem : AbsMessageItem<MessageLiveLocation
     override fun bind(holder: Holder) {
         super.bind(holder)
         renderSendState(holder.view, null)
-        bindMap(holder)
-        bindBottomBanner(holder)
-    }
-
-    private fun bindMap(holder: Holder) {
-        val messageLayout = attributes.informationData.messageLayout
-        val mapCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
-            messageLayout.cornersRadius.granularRoundedCorners()
-        } else {
-            RoundedCorners(getDefaultLayoutCornerRadiusInDp(holder))
-        }
-        holder.noLocationMapImageView.updateLayoutParams {
-            width = mapWidth
-            height = mapHeight
-        }
-        GlideApp.with(holder.noLocationMapImageView)
-                .load(R.drawable.bg_no_location_map)
-                .transform(mapCornerTransformation)
-                .into(holder.noLocationMapImageView)
-    }
-
-    private fun bindBottomBanner(holder: Holder) {
-        val messageLayout = attributes.informationData.messageLayout
-        val imageCornerTransformation = if (messageLayout is TimelineMessageLayout.Bubble) {
-            GranularRoundedCorners(0f, 0f, messageLayout.cornersRadius.bottomEndRadius, messageLayout.cornersRadius.bottomStartRadius)
-        } else {
-            val bottomCornerRadius = getDefaultLayoutCornerRadiusInDp(holder).toFloat()
-            GranularRoundedCorners(0f, 0f, bottomCornerRadius, bottomCornerRadius)
-        }
-        GlideApp.with(holder.bannerImageView)
-                .load(ColorDrawable(ThemeUtils.getColor(holder.bannerImageView.context, R.attr.colorSurface)))
-                .transform(imageCornerTransformation)
-                .into(holder.bannerImageView)
-    }
-
-    private fun getDefaultLayoutCornerRadiusInDp(holder: Holder): Int {
-        val dimensionConverter = DimensionConverter(holder.view.resources)
-        return dimensionConverter.dpToPx(8)
+        bindMap(holder.noLocationMapImageView, mapWidth, mapHeight, attributes.informationData.messageLayout)
+        bindBottomBanner(holder.bannerImageView, attributes.informationData.messageLayout)
     }
 
     override fun getViewStubId() = STUB_ID
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
index e26713b484..e5d2f91404 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -20,7 +20,7 @@
         android:layout_width="0dp"
         android:layout_height="48dp"
         android:alpha="0.85"
-        android:src="@color/element_background_light"
+        android:src="?colorSurface"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
         app:layout_constraintStart_toStartOf="@id/locationLiveInactiveMap"
@@ -30,7 +30,6 @@
         android:id="@+id/locationLiveInactiveIcon"
         android:layout_width="0dp"
         android:layout_height="65dp"
-        android:alpha="0.85"
         android:src="@drawable/ic_attachment_location_white"
         app:layout_constraintBottom_toTopOf="@id/locationLiveInactiveVerticalCenter"
         app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
@@ -40,9 +39,10 @@
 
     <ImageView
         android:id="@+id/locationLiveInactiveBannerIcon"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:layout_marginHorizontal="8dp"
+        android:layout_width="26dp"
+        android:layout_height="26dp"
+        android:layout_marginVertical="8dp"
+        android:layout_marginStart="8dp"
         android:background="@drawable/circle"
         android:backgroundTint="?vctr_content_quaternary"
         android:padding="3dp"
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index 199d36cfde..104063782b 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -20,7 +20,7 @@
         android:layout_width="0dp"
         android:layout_height="48dp"
         android:alpha="0.85"
-        android:src="@color/element_background_light"
+        android:src="?colorSurface"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveStartMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveStartMap"
         app:layout_constraintStart_toStartOf="@id/locationLiveStartMap"
@@ -28,9 +28,10 @@
 
     <ImageView
         android:id="@+id/locationLiveStartIcon"
-        android:layout_width="32dp"
-        android:layout_height="32dp"
-        android:layout_marginHorizontal="8dp"
+        android:layout_width="26dp"
+        android:layout_height="26dp"
+        android:layout_marginVertical="8dp"
+        android:layout_marginStart="8dp"
         android:background="@drawable/circle"
         android:backgroundTint="?vctr_content_quaternary"
         android:padding="3dp"
diff --git a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
index 355d5fa7fe..3f08fae131 100644
--- a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
+++ b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
@@ -65,6 +65,12 @@
         android:layout_height="wrap_content"
         android:layout="@layout/item_timeline_event_live_location_start_stub" />
 
+    <ViewStub
+        android:id="@+id/messageContentLiveLocationInactiveStub"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout="@layout/item_timeline_event_live_location_inactive_stub" />
+
 </FrameLayout>
 
 

From 889e09a891511350d4bd1d2f16e23aa6dc9ae9ef Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 3 May 2022 11:16:11 +0200
Subject: [PATCH 26/45] Adding changelog entry

---
 changelog.d/5689.wip | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 changelog.d/5689.wip

diff --git a/changelog.d/5689.wip b/changelog.d/5689.wip
new file mode 100644
index 0000000000..ccea1ec541
--- /dev/null
+++ b/changelog.d/5689.wip
@@ -0,0 +1 @@
+[Live location sharing] Update message in timeline during the live

From f2b66c5fb5224e08e5b0b972bab927eabf283d86 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 3 May 2022 11:46:51 +0200
Subject: [PATCH 27/45] Fix pin position in static map

---
 .../layout/item_timeline_event_location_stub.xml    | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/res/layout/item_timeline_event_location_stub.xml b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
index 656743f26c..d99ac9fa26 100644
--- a/vector/src/main/res/layout/item_timeline_event_location_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_location_stub.xml
@@ -21,13 +21,13 @@
         android:layout_width="51dp"
         android:layout_height="55dp"
         android:layout_gravity="center"
-        android:layout_marginBottom="28dp"
         android:importantForAccessibility="no"
         android:src="@drawable/bg_map_user_pin"
-        app:layout_constraintBottom_toBottomOf="@id/staticMapImageView"
+        app:layout_constraintBottom_toTopOf="@id/staticMapVerticalCenter"
         app:layout_constraintEnd_toEndOf="@id/staticMapImageView"
         app:layout_constraintStart_toStartOf="@id/staticMapImageView"
-        app:layout_constraintTop_toTopOf="@id/staticMapImageView" />
+        app:layout_constraintTop_toTopOf="@id/staticMapImageView"
+        app:layout_constraintVertical_bias="1.0" />
 
     <TextView
         android:id="@+id/staticMapErrorTextView"
@@ -55,4 +55,11 @@
         app:layout_constraintStart_toStartOf="@id/staticMapImageView"
         tools:visibility="visible" />
 
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/staticMapVerticalCenter"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="0.5" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>

From 0a21bd4b78b00c10a130f619ff92887a673d39e3 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 3 May 2022 12:02:05 +0200
Subject: [PATCH 28/45] Fix crash when mixing static and live location

---
 .../room/detail/timeline/item/AbsMessageLocationItem.kt  | 8 ++------
 .../room/detail/timeline/item/MessageLiveLocationItem.kt | 8 +++++++-
 .../room/detail/timeline/item/MessageLocationItem.kt     | 9 ++++++++-
 .../layout/item_timeline_event_view_stubs_container.xml  | 6 ++++++
 4 files changed, 23 insertions(+), 8 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
index ad18759fc6..b502d82829 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.item
 import android.graphics.drawable.Drawable
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.annotation.IdRes
 import androidx.core.view.isVisible
 import androidx.core.view.updateLayoutParams
 import com.airbnb.epoxy.EpoxyAttribute
@@ -101,15 +102,10 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder> : AbsMe
                 .into(holder.staticMapImageView)
     }
 
-    override fun getViewStubId() = STUB_ID
-
-    open class Holder : AbsMessageItem.Holder(STUB_ID) {
+    abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
         val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
         val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
         val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView)
     }
 
-    companion object {
-        private const val STUB_ID = R.id.messageContentLocationStub
-    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index d68e091564..daca48e7aa 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -110,7 +110,13 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
     private fun getRemainingTimeOfLiveInMillis() =
             (endOfLiveDateTime?.toTimestamp() ?: 0) - LocalDateTime.now().toTimestamp()
 
-    class Holder : AbsMessageLocationItem.Holder() {
+    override fun getViewStubId() = STUB_ID
+
+    class Holder : AbsMessageLocationItem.Holder(STUB_ID) {
         val locationLiveMessageBanner by bind<LocationLiveMessageBannerView>(R.id.locationLiveMessageBanner)
     }
+
+    companion object {
+        private const val STUB_ID = R.id.messageContentLiveLocationStub
+    }
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
index 7da9149dc4..37f728407b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLocationItem.kt
@@ -21,5 +21,12 @@ import im.vector.app.R
 
 @EpoxyModelClass(layout = R.layout.item_timeline_event_base)
 abstract class MessageLocationItem : AbsMessageLocationItem<MessageLocationItem.Holder>() {
-    class Holder : AbsMessageLocationItem.Holder()
+
+    override fun getViewStubId() = STUB_ID
+
+    class Holder : AbsMessageLocationItem.Holder(STUB_ID)
+
+    companion object {
+        private const val STUB_ID = R.id.messageContentLocationStub
+    }
 }
diff --git a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
index 3f08fae131..0d45a48b9b 100644
--- a/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
+++ b/vector/src/main/res/layout/item_timeline_event_view_stubs_container.xml
@@ -59,6 +59,12 @@
         android:layout_height="wrap_content"
         android:layout="@layout/item_timeline_event_location_stub" />
 
+    <ViewStub
+        android:id="@+id/messageContentLiveLocationStub"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout="@layout/item_timeline_event_location_stub" />
+
     <ViewStub
         android:id="@+id/messageContentLiveLocationStartStub"
         android:layout_width="match_parent"

From 6622651a90fa80ff7bdbe2c9baf15630741c7316 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 4 May 2022 16:02:23 +0200
Subject: [PATCH 29/45] Fix send of the first location after start

---
 .../location/LocationSharingService.kt        | 64 +++++++++++--------
 .../app/features/location/LocationTracker.kt  | 16 ++++-
 2 files changed, 51 insertions(+), 29 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
index 362b82ccf5..8b9a1c75ae 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationSharingService.kt
@@ -55,7 +55,10 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
 
     private val binder = LocalBinder()
 
-    private var roomArgsList = mutableListOf<RoomArgs>()
+    /**
+     * Keep track of a map between beacon event Id starting the live and RoomArgs.
+     */
+    private var roomArgsMap = mutableMapOf<String, RoomArgs>()
     private var timers = mutableListOf<Timer>()
 
     override fun onCreate() {
@@ -73,8 +76,6 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
         Timber.i("### LocationSharingService.onStartCommand. sessionId - roomId ${roomArgs?.sessionId} - ${roomArgs?.roomId}")
 
         if (roomArgs != null) {
-            roomArgsList.add(roomArgs)
-
             // Show a sticky notification
             val notification = notificationUtils.buildLiveLocationSharingNotification()
             startForeground(roomArgs.roomId.hashCode(), notification)
@@ -87,7 +88,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
                     .getSafeActiveSession()
                     ?.let { session ->
                         session.coroutineScope.launch(session.coroutineDispatchers.io) {
-                            sendLiveBeaconInfo(session, roomArgs)
+                            sendStartingLiveBeaconInfo(session, roomArgs)
                         }
                     }
         }
@@ -95,7 +96,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
         return START_STICKY
     }
 
-    private suspend fun sendLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
+    private suspend fun sendStartingLiveBeaconInfo(session: Session, roomArgs: RoomArgs) {
         val beaconContent = MessageBeaconInfoContent(
                 timeout = roomArgs.durationMillis,
                 isLive = true,
@@ -103,7 +104,7 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
         ).toContent()
 
         val stateKey = session.myUserId
-        session
+        val beaconEventId = session
                 .getRoom(roomArgs.roomId)
                 ?.stateService()
                 ?.sendStateEvent(
@@ -111,6 +112,16 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
                         stateKey = stateKey,
                         body = beaconContent
                 )
+
+        beaconEventId
+                ?.takeUnless { it.isEmpty() }
+                ?.let {
+                    roomArgsMap[it] = roomArgs
+                    locationTracker.requestLastKnownLocation()
+                }
+                ?: run {
+                    Timber.w("### LocationSharingService.sendStartingLiveBeaconInfo error, no received beacon info id")
+                }
     }
 
     private fun scheduleTimer(roomId: String, durationMillis: Long) {
@@ -134,9 +145,13 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
         // Send a new beacon info state by setting live field as false
         sendStoppedBeaconInfo(roomId)
 
-        synchronized(roomArgsList) {
-            roomArgsList.removeAll { it.roomId == roomId }
-            if (roomArgsList.isEmpty()) {
+        synchronized(roomArgsMap) {
+            val beaconIds = roomArgsMap
+                    .filter { it.value.roomId == roomId }
+                    .map { it.key }
+            beaconIds.forEach { roomArgsMap.remove(it) }
+
+            if (roomArgsMap.isEmpty()) {
                 Timber.i("### LocationSharingService. Destroying self, time is up for all rooms")
                 destroyMe()
             }
@@ -156,16 +171,17 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
     override fun onLocationUpdate(locationData: LocationData) {
         Timber.i("### LocationSharingService.onLocationUpdate. Uncertainty: ${locationData.uncertainty}")
 
-        val session = activeSessionHolder.getSafeActiveSession()
         // Emit location update to all rooms in which live location sharing is active
-        session?.coroutineScope?.launch(session.coroutineDispatchers.io) {
-            roomArgsList.toList().forEach { roomArg ->
-                sendLiveLocation(roomArg.roomId, locationData)
-            }
+        roomArgsMap.toMap().forEach { item ->
+            sendLiveLocation(item.value.roomId, item.key, locationData)
         }
     }
 
-    private suspend fun sendLiveLocation(roomId: String, locationData: LocationData) {
+    private fun sendLiveLocation(
+            roomId: String,
+            beaconInfoEventId: String,
+            locationData: LocationData
+    ) {
         val session = activeSessionHolder.getSafeActiveSession()
         val room = session?.getRoom(roomId)
         val userId = session?.myUserId
@@ -174,18 +190,12 @@ class LocationSharingService : VectorService(), LocationTracker.Callback {
             return
         }
 
-        room
-                .stateService()
-                .getLiveLocationBeaconInfo(userId, true)
-                ?.eventId
-                ?.let {
-                    room.sendService().sendLiveLocation(
-                            beaconInfoEventId = it,
-                            latitude = locationData.latitude,
-                            longitude = locationData.longitude,
-                            uncertainty = locationData.uncertainty
-                    )
-                }
+        room.sendService().sendLiveLocation(
+                beaconInfoEventId = beaconInfoEventId,
+                latitude = locationData.latitude,
+                longitude = locationData.longitude,
+                uncertainty = locationData.uncertainty
+        )
     }
 
     override fun onLocationProviderIsNotAvailable() {
diff --git a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
index b7006370a6..4e56e7954c 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationTracker.kt
@@ -40,10 +40,12 @@ class LocationTracker @Inject constructor(
         fun onLocationProviderIsNotAvailable()
     }
 
-    private var callbacks = mutableListOf<Callback>()
+    private val callbacks = mutableListOf<Callback>()
 
     private var hasGpsProviderLiveLocation = false
 
+    private var lastLocation: LocationData? = null
+
     @RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
     fun start() {
         Timber.d("## LocationTracker. start()")
@@ -92,6 +94,14 @@ class LocationTracker @Inject constructor(
         callbacks.clear()
     }
 
+    /**
+     * Request the last known location. It will be given async through Callback.
+     * Please ensure adding a callback to receive the value.
+     */
+    fun requestLastKnownLocation() {
+        lastLocation?.let { location -> callbacks.forEach { it.onLocationUpdate(location) } }
+    }
+
     fun addCallback(callback: Callback) {
         if (!callbacks.contains(callback)) {
             callbacks.add(callback)
@@ -127,7 +137,9 @@ class LocationTracker @Inject constructor(
                 }
             }
         }
-        callbacks.forEach { it.onLocationUpdate(location.toLocationData()) }
+        val locationData = location.toLocationData()
+        lastLocation = locationData
+        callbacks.forEach { it.onLocationUpdate(locationData) }
     }
 
     override fun onProviderDisabled(provider: String) {

From 82cbc351e502292a3c21fd68977a41dc1a261c42 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 4 May 2022 17:17:18 +0200
Subject: [PATCH 30/45] Fix background color of bottom banner

---
 .../timeline/item/DefaultLiveLocationShareStatusItem.kt    | 2 +-
 .../location/live/LocationLiveMessageBannerView.kt         | 7 +++++--
 .../item_timeline_event_live_location_inactive_stub.xml    | 2 +-
 .../item_timeline_event_live_location_start_stub.xml       | 2 +-
 .../main/res/layout/view_location_live_message_banner.xml  | 2 +-
 5 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
index e453b01692..c421efda12 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/DefaultLiveLocationShareStatusItem.kt
@@ -68,7 +68,7 @@ class DefaultLiveLocationShareStatusItem : LiveLocationShareStatusItem {
             GranularRoundedCorners(0f, 0f, bottomCornerRadius, bottomCornerRadius)
         }
         GlideApp.with(bannerImageView)
-                .load(ColorDrawable(ThemeUtils.getColor(bannerImageView.context, R.attr.colorSurface)))
+                .load(ColorDrawable(ThemeUtils.getColor(bannerImageView.context, android.R.attr.colorBackground)))
                 .transform(imageCornerTransformation)
                 .into(bannerImageView)
     }
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 7d008e0fc6..37174c8442 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -88,7 +88,7 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
         }
 
         GlideApp.with(context)
-                .load(ColorDrawable(ThemeUtils.getColor(context, R.attr.colorSurface)))
+                .load(ColorDrawable(ThemeUtils.getColor(context, android.R.attr.colorBackground)))
                 .transform(GranularRoundedCorners(0f, 0f, viewState.bottomEndCornerRadiusInDp, viewState.bottomStartCornerRadiusInDp))
                 .into(background)
     }
@@ -108,7 +108,10 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
                         }
 
                         override fun onFinish() {
-                            subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L)))
+                            subTitle.text = context.getString(
+                                    R.string.location_share_live_remaining_time,
+                                    TextUtils.formatDurationWithUnits(context, Duration.ofMillis(0L))
+                            )
                         }
                     }
                     countDownTimer?.start()
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
index e5d2f91404..248a1b9078 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -20,7 +20,7 @@
         android:layout_width="0dp"
         android:layout_height="48dp"
         android:alpha="0.85"
-        android:src="?colorSurface"
+        android:src="?android:colorBackground"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
         app:layout_constraintStart_toStartOf="@id/locationLiveInactiveMap"
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index 104063782b..741853c9a7 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -20,7 +20,7 @@
         android:layout_width="0dp"
         android:layout_height="48dp"
         android:alpha="0.85"
-        android:src="?colorSurface"
+        android:src="?android:colorBackground"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveStartMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveStartMap"
         app:layout_constraintStart_toStartOf="@id/locationLiveStartMap"
diff --git a/vector/src/main/res/layout/view_location_live_message_banner.xml b/vector/src/main/res/layout/view_location_live_message_banner.xml
index 42004a5b81..a63ddac767 100644
--- a/vector/src/main/res/layout/view_location_live_message_banner.xml
+++ b/vector/src/main/res/layout/view_location_live_message_banner.xml
@@ -14,7 +14,7 @@
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"
-        tools:background="?colorSurface"
+        tools:background="?android:colorBackground"
         tools:ignore="ContentDescription" />
 
     <ImageView

From 4862f8e7c9da0e06213a0ccd13ba2c030505cc26 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 9 May 2022 14:46:45 +0200
Subject: [PATCH 31/45] Dark mode support for no location map background

---
 .../drawable-night-hdpi/bg_no_location_map.webp  | Bin 0 -> 474 bytes
 .../drawable-night-mdpi/bg_no_location_map.webp  | Bin 0 -> 320 bytes
 .../drawable-night-xhdpi/bg_no_location_map.webp | Bin 0 -> 1062 bytes
 .../bg_no_location_map.webp                      | Bin 0 -> 1446 bytes
 .../bg_no_location_map.webp                      | Bin 0 -> 1690 bytes
 .../bg_no_location_map.webp                      | Bin
 .../bg_no_location_map.webp                      | Bin
 .../bg_no_location_map.webp                      | Bin
 .../bg_no_location_map.webp                      | Bin
 .../bg_no_location_map.webp                      | Bin
 10 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp
 create mode 100644 vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp
 create mode 100644 vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp
 create mode 100644 vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp
 create mode 100644 vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp
 rename vector/src/main/res/{drawable-hdpi => drawable-notnight-hdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-mdpi => drawable-notnight-mdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-xhdpi => drawable-notnight-xhdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-xxhdpi => drawable-notnight-xxhdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-xxxhdpi => drawable-notnight-xxxhdpi}/bg_no_location_map.webp (100%)

diff --git a/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000000000000000000000000000000000..5c0d2e39d55ba843721e60c64fac277efacec6eb
GIT binary patch
literal 474
zcmV<00VV!YNk&G}0RRA3MM6+kP&il$0000G0002h005Ez06|PpNOJ%H00DQPG?FAW
zdf?A8ByjFOA_6FIKgv@)W(K3$ww0{k0IOgP7*oU}FxLPSummguXaJ4C%&p0t@+0~`
z0gT&!7!#3b$A>Z&q8G-ZtRGJA0#e>U=8yWH`k(s$cjE)3yMW|75IuTfEQ)r(hy})}
zKolwg09H^qAUFX401z1fodGJ=0FwYdZ8nxkBqJgrAWEQs4T))P;cWrOpaO?!&}yox
zcpq}+xjNssS+9ooEQpARcSOd?!;E^+oXWl;oyI`hW0oSFrIhoN@Q-S5d{mOnX}XPw
zjmsh;BW&MFj9=^^`6E!>+WsPrZX?Ay4OG>O*_k+EJkotE3AgfMN4D&n+q`zUHmD#u
z;uk|Amstx)&=hCN1aR$9q=V0mx5J+-zh6)W01ir&8mZDOXV83Jf5!~C8zma8*3h{k
zH&Pn~A(VB)Ztlj`V(b9^{mW@^G4b>L;6}|@%tM5ei0yc~i-8S(&&ViX3K+1=;;gfQ
z(W^vAv7oSi`S)FuTYipA&~1#wzdoQqO+q~aU+kNNfFLx|Xb|jh4X=~R$QM@PBOG1t
QFY5{{A{q<IbM!=x0KS#ek^lez

literal 0
HcmV?d00001

diff --git a/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000000000000000000000000000000000..3c17c090d6b58edd123695e0ae55e9bd9a60792d
GIT binary patch
literal 320
zcmV-G0l)rINk&FE0RRA3MM6+kP&il$0000G0001v003bC06|PpNJ#(y00BRsAOIOg
zDJ=zNw?;$+-b2lUZDiX@zW-RswE&MD`tR$+5Yhh$*!n;?ykZWFgQ*~bO%y6n%HMw<
zLL!AO6n=&n%(f!bw*UZEP&gpS0000m4*;D3DvtnS06uLtmPn){A|W7)xPT3bX#f`5
z03L@p(OX6jS5M-rI+DWd#C^(KeK7ti0^PH)No>b&JLV9X{ndr_TD&3!J@^{0CW>YR
z=^ASM)j~SxRUtTMCMq^@0t>_#E>&|H>)&;!aLl1?4(-u0(=Yg5#ed}gN*SXJ0092~
zVhUpTiMi=;C=J*-$XDy|3=}-czUo=W&md^U@_65cD`-W$O4llG7n*f>u8UPPPzpc~
SY_ot9xV;7gqGuZL0002jrGMQ3

literal 0
HcmV?d00001

diff --git a/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000000000000000000000000000000000..b79c747ac141234d4ff079f871756c0bc1b8aa8e
GIT binary patch
literal 1062
zcmV+>1ljviNk&E<1ONb6MM6+kP&il$0000G0000T0RY1Q06|PpNOk}K00Cb>AV8Va
z0w#E1iiij$J!ZIYTido0T`cz&JSK8Fu#ghSXycba31m9f<Bevm7t#L-0H*JcMHK?*
zeoXqXKU65K{c&j?WE3wl<~JGpPy8?b7ypa@#sA{}zw2Kz)J?{6l9BafSoWKWO~XJ%
z0APL7u;&0)P&goh0{{R}B><fPDjosE06qx<fk1&i000n{mhZ>Oo?d;dU+OLQ>z;T1
z`{E9|TgUyz=nI3HIT>r)Mi+W6k+3%rW0;c1F%!!T8rQG#t+#5{2q^uWQTsTfwK%}V
z_mpYIIxU=0`!)?hJ{GZ@^UWGgT<PBAaTV@8gr?-l3y$slYqIf5lm&o|BTY6Jn3Bga
z93p_;Y^QpfM0TV%_whQg<LPL&aYy7W9j=9!Mp4?K9yt1b7mGVrxb~z72SK=Sxq*`@
zYZ{Q1=8b)`iWTeAQMV!{2SOdx)z{xMcwp0~1pZw<3c65O*g~|>;6>ye3c`Dg_6sgg
zJ#EBSxODfy)@NrNtyXa9r91FeEuuwHIv!?OU#2_oW5%KTIG}kk>rXnygHDHXHC(K6
z+fDQKaYyMY=l}rz|Nn%9NP3m><{<Zz7vKMqVBy^Ce?3rt?Qj@vy6iWffVz8-8O;!#
zV})6t*ivk%YQ!J|H+ohdHp~;Jr4Z*fVH&jp8k-m<Z+xI&nLakK*_w`Z*irlIeKAT-
zd8^7xJqo~y2OiaEOqKv-^}GLsInk2A+2Z!{l)(u}ivz!XhPW+Glzu)ywGv;`wX_G=
z?u>u^qE&1pvyTHfNeo#)-iN#@5@+;90H9E>s-MtH5Ywldp5+xrL>_-SWwONMZH!dS
zXO?vf1Rf=Wd<@njEqOBtie-9$mNU+zd15}=jlHs-=JDFT`r37x$z{m6ZDFx`3Q_r7
zL|ftC$2(XyDt|ZnS2qJWqZ}iJ406MJR0bNdR`()%^`pUl+)~@3jwHR9TY6Qb8hI_|
z9S)}r;Y@_7D(NKZbjC-kuw&Y0G;<OiYg({$6}qFw)z++HG1hStpRD4Tdr8-N?T$i?
zc~#;<9@$%Zybq1`h|Eg3R?P$I_nWseKwRpoLDYfSFhQWjxJbx`!Q^k+1CRV;#p}p^
zsT4xrhkNvB?pc5<xPY`DwT!)ip}tabBIRCL-fR`!@A3=8Rb5jZ;EnJwsN$+Wz0rcv
zXHp#2tDVNXnqF?<$`1p3SFHQRdQ;uu7O*P<k?xtB;hoc2@H$=F)(x+x8I|$w-+Zbg
zvhlXjs1$5(c7V6sSx3SjH>>yYd}r32QCpNG%&~gnsZ0L_{_C(^t{31y<)u$x!w{!p
gE5aGbl8zg3uU7XqnVDH^io=tQQy_isIrf+U0FloJP5=M^

literal 0
HcmV?d00001

diff --git a/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000000000000000000000000000000000..75aa5cecc6c6feecc7168a8522d1b3f086630c60
GIT binary patch
literal 1446
zcma)6`8U)H6#ovg)jVUe6v>P-*3qNPSYDQ4gvf5>*@YH#8jW>Ar6Fw?^ei(;N2FPv
zB>UE}#3W0$ESa)IF$S5jW!}7cf57|U-h1x3=X~zxb3f-?S34UU>K*{VVKL60&PHC+
z002OQK?4EmAi&bW*_JQtK*`<_3GNHzOf68JL548fp?6e#O#Z5p^qB`Xr#XqI7!IJR
zaDQtD05$$G_lJ&7Bd1Q>KQ+7a%LM`EPUVOxODnwHpkXyibHy!fZL8avsSiA(g{2oZ
z9)-y;byW<M5@r`hqxsfxoX$K=vWG@@;>vLsKOS?ndFPmbCHVg(>Fct88lD&8B*#R_
zmT<PhiYEw5wsMy;?jdroyYDz;u6aWtL4<T#)II=mN4CwrE!6e{C(Qi)=MeLg4~%ai
z0Y|iFX?tQtQ#%3>UIdmb1^~ZX02Gim9fSax9o%;d0~Z77hyj5}<b>WZtRr$ll*Z(R
zY56Zwv%P1>V&y1>EL-H{;I3PXXBDT-l+28VPf!vVBehT+5#RA18NWrVS`&VdO4ms3
z;rT2M5$*z4!$|ZHQ41sW5Y8DkDyEDzYqgXDM^Z*p@r0icHucOLHnX_Acg`zCCVwO1
zJzd#Fa--rh)uR@!@wNvFK~Dxt8FI%<h2W))UeK?`O)l1VDe(#mUh^y65FOS{iC<I9
z@XABOqwY{PdeEx$$PQ-Jx1r2(`#*|B7aMzQ@^TOHgCu$^pJwvBZDjqr&itaQsI*P5
z9;NBIUF#Z7C2p~CUwI_^MERd9jRxF&Bvlgrp7J0u?o0Wu$B`z50u=_02lp@@oxL6l
zW5p(zW+_<Kx2E0l|AYOfkR^tVU2?7Y)VM$j0UsJ>L8g`rj@>q`peLDigqdJ<No)S#
zT!@*}L~shZpu9b_4$0UKcT{$&;x_-#Z(oS=<MlpOov)qo8;geVfydMX1ke4&PT)EM
zr>?&If#_0Ct#zr&r%ls}y)o$viF!J)<Et^&rkR2g(&&|<g2IrOEt^adOGE!PcAh_a
zq4G_Pn5&zETTqp3=)Wc`ea~DcpM+Q2V%b<L5t<QQXiMX0svi{qb_FY)Q@gAKCSi-u
zi|AKrFYWQ%-~QmrUI9jG*;szlLGm}bOD5chlR9=_`_{M^VqP#_Ixul<`N=e37?3+R
zYdzcL^zpQ4Qi57hi@fz4L7&N7xBa@q6h8yRrVtiAVs<Y-c5yI_ZUU=9scMLW_;Hlu
z)nSQ}c?>x+)q`J;vshPeIp?TRcQR1YOlqJo$Rex#aX<ifwxGk`<2amm*Zs}bWL{D}
znsWcOSoE%|)=YOS(3&j>z!RwIhoKm;p&E4OKwXPSoN0ML#j537mCrNWxw-fh)K1ro
zvu_-AVermJc{7C@-O&~zmz1!s)HZR6!$#I8ygV~ERzy}C7$00a=5}A<M$9!7<5%@R
zw~|@$bEtm1-e52wCoO88l#|@^j92ZbFm&zJ-1lmJRe1CEVBe9B40LwZmnU1U1e|>;
zt^I5`a$TIdciCBPU`I7FHd?H1<R}_=SNdXRyzjIOH}c09<yhj|8nqqG!ho@CS(!YX
zx<E-kOgDA&hNVOk_1>$fv1LYbAAdSl`kd|_?cIt@T5@+8-k?7MNXmi0WX=yETXTK%
zz~B?Ip#%2X2H@agy>uVtoI?(HnHofsxYu{VOK0h+<)S5pX8Guw!ShhB%9+g;*?jKw
zN_u@O6=Arq^8UvDLV~!B;@NM;@qzQ=NmuE(!P8^59lhK6M3Zlfw0r|~TsvB<TdSIs
zyFX~?9YPT<;aE8aV`!2&vH0u#ITzKhp`d01W|N#T0WQDuqU!Sp5e1AFXY@;?nv{Or
tz-~88Bf{Nb#4=ppVE=7$Z<)xW(o70Z8#P2EDxG?M{^_s|JwW(>{{eA<+CBgP

literal 0
HcmV?d00001

diff --git a/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp
new file mode 100644
index 0000000000000000000000000000000000000000..511d237d4bf8a6637a90a936da6bb88879048023
GIT binary patch
literal 1690
zcmaJ<eLT~79RJxy5odLtcC*YZPgi+JR5p+E)REu1tKFSBhBiV}H&LC3$+M7$LM0DZ
zoTz0Oorefpg+j==A$G>iIclUe+b!Ms^Zxk0U*FH`_4$0?@6Y@DKI-A>nyUl=Bo`;}
zIC$V&6#xL>@?#4DQWOA3Ptg6gyi)LUKsyw@gcwHqr>Ab+g{L+}9veS??50X3M6=2v
z>FW-a<E0Gw%}#c0f|EWcgAoX#?~$CC`lb^Goa8fDbWHee+8Yb&LTGm5z`~C=!Zc8M
zmt8m`GO?db7`J|3w$d&g{hy-UPaBz+6);m;!5WxL#Z*ADZ;;7P+=7TiL_DGX>>Fk^
zXAc69>qqw}0s!-S;HH8p4w9;1XQ7ip!04C3VVBTQSUo!y8aEyvceYxR8(|?1g%5g^
z-7U0TmZV+Flc=wGgL=Ltny)6O^!(QK7G=+5-Yd2vzsh9ebIQ`>C)#?#eLD`cZ@~Qh
zP^vFaEqAZ|2o-xeZUdNowX;=4=8O+gmW4saCM2S+(r-+^au|2hUZ)0Z0;Szx#w*<W
zlGf(@#v`*Py;q%$9zou*or5y6SBlbf{Wd_j9b#uSdKuG%`<HFR=t4Kyg(L2L3kPa%
zp*9IskxqXzG#r~E4)u0rsYVrtXcn5Zbr|}6(l|mjx=9?KY8^O~$~6u<@^mXJkgeF9
zJM*71ob4aYe#uRV9IZQ<hWo)%!hdX3B%BNONS8d_r)4L)FyDmD4?_i8vdqMC7Bg{I
z8IGwgc_TLjO)p1}#nOW}7Y*QwBNegcB|c?!=BJv=O(tG!8WC{#UDMX`kDXS*d!D~Z
zwp$5*h{TfPYMup{R-P<{GzJ=^t01v+%K6pWL@Vg^fKM_fta5li)n+zm$e^UEZFOUq
z8L3WbRNCv-!QVQ!%D1|5^0NzL-2}v}aZuR3XEpuZlY(izuP#Vg=KS=|C>dW)&>?B5
za$j~WniSy_RFlLrXwoiq0!1R9JuUyYog8sH6dqT{Ju6rJ00;MK5DkKInsq9rMZh&y
z%K=kFG?Lu<u5${n*}FeWf_Clvm<u&N-Z*HBaO#S9U?VFQwFFLo(=YYs<sR)8RH)?8
za@I6I1*pLnb8`Nolc#)P%!lRdGr#wK-q0xt&17uE%yQhes5OyPa=`!R6H0yst*2tD
zk2%hKiqzk003coO5gWxgB_-^Oj*Tt#A*QOGfkI3Krp18vdyvu=9H!KQx@^<eeVu_U
zDEMo%C5u|_L)sE$6}8~MKhn{yRTkC;pi?_l0w2+8ay9e69f?Y45M-_uG7ORj+cXYc
z@>U(NOq`A||KUX>rn>|iij0;Ok2RhmNACTyxq_Me_}Y@^%aym=*=982Md0i0ZZ4F&
zxqJ!Oz|6`F#l!9vAq;)}zl6aJQ{GQQ_PH4`?y*R@jK?($XW~(P|5#!C(LSYZqpOhb
z2EWlNPCKVHFI~g^!G0Nw?;K-Qyn?;p=#t@$*4%}@I(v0po7vT~YEC@_MLXTXK(6R^
zHcCHF2{P1^>^QnEw7#kHM7EQ^C=UPJEg#_FJ6><I<I$jG*TO40b3tAqB_Y~ZR_<9p
zB*uGF-nrBv%OXH*6zjg_X~R74gKI`tF_T?)Z}IMA!Eb2oL8$fivChma*-S(~PfPRG
z>VMRE7{0uihFcDFd7!$=a^@OE+-f$o!6~)mjD_g?P)1CwGKhU4*xvL@Q}>03XhEWR
z#MEF-DHG6m%e2PYqf;uEA0hq>`_4c5SqA>E7g`)x<yT(k)~r#d94Jh>=@-6zi=HEM
z2Fv!S%2myj)ep_0BHa_VdgpAAKHCxn>YSqim50s3A87o#_f}?}03jB+f)G-9(&7L@
zf`20MB}3f0hOm(y9~}h&pCMYu7fTC^SxfsT3WW4f)tp2#_e4=u8y$5aqvzp~WBIi?
zygFNNqkH^iI{(Ava(23~1V$%|e#~+NjqBKE!<N?%@=eDmY~|otz2H|7za*UZmktGj
zPX$iOyx|1n(u-K@9Wi>>*L|OQ$m$EJV`}3D#gC<`Pn{#D3d(G0#mW<AoZ<e*{4f&P
z9v_0&M_qF!=p4`Eg8lrO>efcD-UQgBhP}u`6FH%1ImEoZ$|FrX?~PBpN?(X_I}f6=
U5Au9g2c8cqzMloAQ_ukL4>k=#xBvhE

literal 0
HcmV?d00001

diff --git a/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-notnight-hdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-hdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-notnight-hdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-notnight-mdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-mdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-notnight-mdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-notnight-xhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-notnight-xhdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-notnight-xxhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-notnight-xxhdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-notnight-xxxhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-notnight-xxxhdpi/bg_no_location_map.webp

From 16be69ebaeffd92a4c3b23f944d2bd06707d2aaa Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 9 May 2022 14:56:35 +0200
Subject: [PATCH 32/45] Fix tint of banner icon for start and inactive message

---
 .../layout/item_timeline_event_live_location_inactive_stub.xml   | 1 +
 .../res/layout/item_timeline_event_live_location_start_stub.xml  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
index 248a1b9078..6b06e05b08 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -50,6 +50,7 @@
         app:layout_constraintStart_toStartOf="@id/locationLiveInactiveBanner"
         app:layout_constraintTop_toTopOf="@id/locationLiveInactiveBanner"
         app:srcCompat="@drawable/ic_attachment_location_live_white"
+        app:tint="?android:colorBackground"
         tools:ignore="ContentDescription" />
 
     <TextView
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index 741853c9a7..505b73ae68 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -39,6 +39,7 @@
         app:layout_constraintStart_toStartOf="@id/locationLiveStartBanner"
         app:layout_constraintTop_toTopOf="@id/locationLiveStartBanner"
         app:srcCompat="@drawable/ic_attachment_location_live_white"
+        app:tint="?android:colorBackground"
         tools:ignore="ContentDescription" />
 
     <TextView

From 59567e39b402fefa60964f14abee2759e53523a8 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 9 May 2022 15:11:47 +0200
Subject: [PATCH 33/45] Fix code quality issues

---
 .../main/java/im/vector/app/core/utils/TextUtils.kt |  6 +++---
 .../detail/timeline/factory/MessageItemFactory.kt   |  7 ++++++-
 .../detail/timeline/item/AbsMessageLocationItem.kt  |  1 -
 .../detail/timeline/item/MessageInformationData.kt  |  2 --
 .../timeline/style/TimelineMessageLayoutFactory.kt  | 13 ++++++++++---
 .../location/live/LocationLiveMessageBannerView.kt  |  5 ++++-
 6 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index f9c33ffe2a..80dabd5021 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -93,18 +93,18 @@ object TextUtils {
         when {
             hours > 0   -> {
                 appendHours(context, builder, hours)
-                if(minutes > 0) {
+                if (minutes > 0) {
                     builder.append(" ")
                     appendMinutes(context, builder, minutes)
                 }
-                if(seconds > 0) {
+                if (seconds > 0) {
                     builder.append(" ")
                     appendSeconds(context, builder, seconds)
                 }
             }
             minutes > 0 -> {
                 appendMinutes(context, builder, minutes)
-                if(seconds > 0) {
+                if (seconds > 0) {
                     builder.append(" ")
                     appendSeconds(context, builder, seconds)
                 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index 868018a706..ea703a0d11 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -216,7 +216,12 @@ class MessageItemFactory @Inject constructor(
                     buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
                 }
             }
-            is MessageBeaconInfoContent          -> liveLocationShareMessageItemFactory.create(informationData.liveLocationShareSummaryData, highlight, attributes)
+            is MessageBeaconInfoContent          ->
+                liveLocationShareMessageItemFactory.create(
+                        informationData.liveLocationShareSummaryData,
+                        highlight,
+                        attributes
+                )
             else                                 -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
         }
         return messageItem?.apply {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
index b502d82829..f7146c24e9 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/AbsMessageLocationItem.kt
@@ -107,5 +107,4 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder> : AbsMe
         val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
         val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView)
     }
-
 }
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
index dae1c3671d..be0f2d55ca 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
@@ -20,8 +20,6 @@ import android.os.Parcelable
 import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.crypto.verification.VerificationState
-import org.matrix.android.sdk.api.session.room.model.message.LocationInfo
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
 import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.api.util.MatrixItem
 
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
index fab7dcb49a..5bb4db6e9b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/style/TimelineMessageLayoutFactory.kt
@@ -66,6 +66,11 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
                 MessageType.MSGTYPE_VIDEO,
                 MessageType.MSGTYPE_BEACON_INFO,
         )
+
+        private val MSG_TYPES_WITH_LOCATION_DATA = setOf(
+                MessageType.MSGTYPE_LOCATION,
+                MessageType.MSGTYPE_BEACON_LOCATION_DATA
+        )
     }
 
     private val cornerRadius: Float by lazy {
@@ -145,9 +150,11 @@ class TimelineMessageLayoutFactory @Inject constructor(private val session: Sess
     }
 
     private fun MessageContent?.timestampInsideMessage(): Boolean {
-        if (this == null) return false
-        if (msgType == MessageType.MSGTYPE_LOCATION || msgType == MessageType.MSGTYPE_BEACON_LOCATION_DATA) return vectorPreferences.labsRenderLocationsInTimeline()
-        return this.msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
+        return when {
+            this == null                            -> false
+            msgType in MSG_TYPES_WITH_LOCATION_DATA -> vectorPreferences.labsRenderLocationsInTimeline()
+            else                                    -> msgType in MSG_TYPES_WITH_TIMESTAMP_INSIDE_MESSAGE
+        }
     }
 
     private fun MessageContent?.shouldAddMessageOverlay(): Boolean {
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index 37174c8442..bb69cdd1c2 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -104,7 +104,10 @@ class LocationLiveMessageBannerView @JvmOverloads constructor(
                     countDownTimer = object : CountDownTimer(it, REMAINING_TIME_COUNTER_INTERVAL_IN_MS) {
                         override fun onTick(millisUntilFinished: Long) {
                             val duration = Duration.ofMillis(millisUntilFinished.coerceAtLeast(0L))
-                            subTitle.text = context.getString(R.string.location_share_live_remaining_time, TextUtils.formatDurationWithUnits(context, duration))
+                            subTitle.text = context.getString(
+                                    R.string.location_share_live_remaining_time,
+                                    TextUtils.formatDurationWithUnits(context, duration)
+                            )
                         }
 
                         override fun onFinish() {

From 7aa958b9ff6df9e3b9dfb62537d963367ef788d0 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 9 May 2022 16:11:36 +0200
Subject: [PATCH 34/45] Fix getting related eventId for location events

---
 .../EventRelationsAggregationProcessor.kt     |  9 ++++++++-
 ...DefaultLiveLocationAggregationProcessor.kt | 19 ++++++++++++-------
 .../LiveLocationAggregationProcessor.kt       |  1 +
 3 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
index 7e0b44a314..0612199502 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
@@ -194,7 +194,14 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
                             }
                             in EventType.BEACON_LOCATION_DATA -> {
                                 event.getClearContent().toModel<MessageBeaconLocationDataContent>(catchError = true)?.let {
-                                    liveLocationAggregationProcessor.handleBeaconLocationData(realm, event, it, roomId, isLocalEcho)
+                                    liveLocationAggregationProcessor.handleBeaconLocationData(
+                                            realm,
+                                            event,
+                                            it,
+                                            roomId,
+                                            event.getRelationContent()?.eventId,
+                                            isLocalEcho
+                                    )
                                 }
                             }
                         }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
index 997e31a109..3ac47ee8cf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
@@ -60,22 +60,27 @@ internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : L
         aggregatedSummary.isActive = content.isLive
     }
 
-    override fun handleBeaconLocationData(realm: Realm, event: Event, content: MessageBeaconLocationDataContent, roomId: String, isLocalEcho: Boolean) {
+    override fun handleBeaconLocationData(
+            realm: Realm,
+            event: Event,
+            content: MessageBeaconLocationDataContent,
+            roomId: String,
+            relatedEventId: String?,
+            isLocalEcho: Boolean
+    ) {
         if (event.senderId.isNullOrEmpty() || isLocalEcho) {
             return
         }
 
-        val targetEventId = content.relatesTo?.eventId
-
-        if (targetEventId.isNullOrEmpty()) {
-            Timber.w("no target event id found for the live location content")
+        if (relatedEventId.isNullOrEmpty()) {
+            Timber.w("no related event id found for the live location content")
             return
         }
 
         val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
                 realm = realm,
                 roomId = roomId,
-                eventId = targetEventId
+                eventId = relatedEventId
         )
         val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
         val currentLocationTimestamp = ContentMapper
@@ -85,7 +90,7 @@ internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : L
                 ?: 0
 
         if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
-            Timber.d("updating last location of the summary of id=$targetEventId")
+            Timber.d("updating last location of the summary of id=$relatedEventId")
             aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
index c0be96f83d..d2450aef9c 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
@@ -35,6 +35,7 @@ internal interface LiveLocationAggregationProcessor {
             event: Event,
             content: MessageBeaconLocationDataContent,
             roomId: String,
+            relatedEventId: String?,
             isLocalEcho: Boolean,
     )
 }

From 738d486f9d9b71a1c5f58ba2a76ba3e503c81fe4 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 9 May 2022 17:31:02 +0200
Subject: [PATCH 35/45] Moving the light "no location" background into default
 folder

---
 .../bg_no_location_map.webp                         | Bin
 .../bg_no_location_map.webp                         | Bin
 .../bg_no_location_map.webp                         | Bin
 .../bg_no_location_map.webp                         | Bin
 .../bg_no_location_map.webp                         | Bin
 5 files changed, 0 insertions(+), 0 deletions(-)
 rename vector/src/main/res/{drawable-notnight-hdpi => drawable-hdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-notnight-mdpi => drawable-mdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-notnight-xhdpi => drawable-xhdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-notnight-xxhdpi => drawable-xxhdpi}/bg_no_location_map.webp (100%)
 rename vector/src/main/res/{drawable-notnight-xxxhdpi => drawable-xxxhdpi}/bg_no_location_map.webp (100%)

diff --git a/vector/src/main/res/drawable-notnight-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-notnight-hdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-hdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-notnight-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-notnight-mdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-mdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-notnight-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-notnight-xhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-notnight-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-notnight-xxhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp
diff --git a/vector/src/main/res/drawable-notnight-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp
similarity index 100%
rename from vector/src/main/res/drawable-notnight-xxxhdpi/bg_no_location_map.webp
rename to vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp

From cb5c6ec35ef12777fc1432573a0021c0263afd12 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 11 May 2022 09:53:42 +0200
Subject: [PATCH 36/45] Decreasing alpha of bottom banner in message view

---
 .../layout/item_timeline_event_live_location_inactive_stub.xml  | 2 +-
 .../res/layout/item_timeline_event_live_location_start_stub.xml | 2 +-
 .../src/main/res/layout/view_location_live_message_banner.xml   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
index 6b06e05b08..d5a0cefb28 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_inactive_stub.xml
@@ -19,7 +19,7 @@
         android:id="@+id/locationLiveInactiveBanner"
         android:layout_width="0dp"
         android:layout_height="48dp"
-        android:alpha="0.85"
+        android:alpha="0.75"
         android:src="?android:colorBackground"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveInactiveMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveInactiveMap"
diff --git a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
index 505b73ae68..1726928721 100644
--- a/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
+++ b/vector/src/main/res/layout/item_timeline_event_live_location_start_stub.xml
@@ -19,7 +19,7 @@
         android:id="@+id/locationLiveStartBanner"
         android:layout_width="0dp"
         android:layout_height="48dp"
-        android:alpha="0.85"
+        android:alpha="0.75"
         android:src="?android:colorBackground"
         app:layout_constraintBottom_toBottomOf="@id/locationLiveStartMap"
         app:layout_constraintEnd_toEndOf="@id/locationLiveStartMap"
diff --git a/vector/src/main/res/layout/view_location_live_message_banner.xml b/vector/src/main/res/layout/view_location_live_message_banner.xml
index a63ddac767..35924541d1 100644
--- a/vector/src/main/res/layout/view_location_live_message_banner.xml
+++ b/vector/src/main/res/layout/view_location_live_message_banner.xml
@@ -10,7 +10,7 @@
         android:id="@+id/locationLiveMessageBannerBackground"
         android:layout_width="0dp"
         android:layout_height="50dp"
-        android:alpha="0.85"
+        android:alpha="0.75"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent"

From b2099b6b31b769b967b2da22f7a14b47bec29cce Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 11 May 2022 10:34:54 +0200
Subject: [PATCH 37/45] Updating the no location map background

---
 .../res/drawable-hdpi/bg_no_location_map.webp | Bin 952 -> 876 bytes
 .../res/drawable-mdpi/bg_no_location_map.webp | Bin 638 -> 594 bytes
 .../bg_no_location_map.webp                   | Bin 474 -> 958 bytes
 .../bg_no_location_map.webp                   | Bin 320 -> 640 bytes
 .../bg_no_location_map.webp                   | Bin 1062 -> 1500 bytes
 .../bg_no_location_map.webp                   | Bin 1446 -> 2022 bytes
 .../bg_no_location_map.webp                   | Bin 1690 -> 4010 bytes
 .../drawable-xhdpi/bg_no_location_map.webp    | Bin 1228 -> 1320 bytes
 .../drawable-xxhdpi/bg_no_location_map.webp   | Bin 1846 -> 1822 bytes
 .../drawable-xxxhdpi/bg_no_location_map.webp  | Bin 2706 -> 3522 bytes
 10 files changed, 0 insertions(+), 0 deletions(-)

diff --git a/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-hdpi/bg_no_location_map.webp
index 23a45700f0a59f6caa118e5b7d6a7216c579b3d8..3241b5dc82fbc19906d1b636b0ade4fae1f77900 100644
GIT binary patch
literal 876
zcmV-y1C#txNk&Fw0{{S5MM6+kP&iCj0{{RojzAa?C&HkSBt<oQ{_Wl~g9fy1+lF@j
zzqD(2Gi=+&5q0|yT)sq6BuPzU0Xxb63n{!as7>2|8PjtI^nbr`fMFak7zb|O_rTx4
z=fi`D!N9=565HmXv5w8pRV%3$g?39CNkWi@dNuv+G(!p;kZx^n+DKKXFyP%X?1U3{
zb_>JIGxvW{i>ma+vp*yH55bKjMN;G*Gh|mzmi7a!_p|lN722G?x861@muT~PzH*Ds
zHjZJv`p?m*b`0eA3%Br72Z>`K>MoAm0#P)!Zh<K1S5DyXGPz?Q+K(LrQG4ESI10zM
z$?)X&%jUw?eb!WVP&alAL{Bs87KnnObqhqkDck~)caS=SKZAy2Ad05eEfD#`s%c2L
zDQg14dW_-ek+eFC@8GxmaJOd~88phiC;dz?KzEZJM<gwiqCYe5h(FVdf2^<-ABaEG
zh7TIKk^ko%*Wk}ogHZe#_-4UQ<lTfHG!{L1Hyv5<XA%WBP0*hy<lPkBR^ZRnBXKu*
zCWCiVTi#7E_%p{h@^0$Rmn&?gM(mm9^kqdqkPjNgXB#w@Gl8JdxV|iYl#kEwK_itv
zk|X$_QQnHX$?t!<Zn7FdW3h5geC|x(<Foubgu@d1{5v3d`FA<;S>yeNJA#HGEp-=|
zL8AyrPZ6Y>2Baz<Z$VZ?q%7a$Sd0lkJtQ~TE<va>hv~}}+|wg5UNhsf?J<d0t8$<a
zG!SM)%st7hTC<R;bHHTwm!Q#=chfgrGLKmf)Vh7y)GE>^9^-!njXU|iY-F+KQuuEx
zRPynez9_}#5%K7=j6aii)1F3ru9-huz#TRU%|d%2&V+ke0MwUZ%q1_`ty%Dqx#9k^
z?P$kp9Ky1EV6qTs7TR*4%q0O`_aHG$rczVH;E^-k5Y>VUvH*+$@fgWA3n3C^z&v^j
z>CD){VN5)}f;9^Ob173=kRdSRrbw+-!Z6^Xf87#m_GQW(aTDzlZ9%49-7M4>WO}6B
zYeuVIx*A^z!(;-nARC4n6x_K4(t#|ekvs$D(;?v6S9vZ;^)a$RLw7FSp|w^0Kn%+~
zD*Xg;m7rVQVBUR2wYq`4uhE%eH1AnN&ObJaa|!b4N9*I_bi1prUU+)G+`{SP?4t#?
C8Lf~2

literal 952
zcmV;p14sN)Nk&Gn0{{S5MM6+kP&il$0000G0002i005By06|PpNNoTB00D3t+qP0Y
zZXG-dv5K77YvR3>@7{^KYY8X35itRRWW9L)k-RNZNrF7}$5VME^#_oTw$eYQ<qVX;
z9hBW4)HC~!{m1@e|FQqre~h~aD626jZ3c>O=?@?uCCO8OG&Tt`<JoeH<a%D1h5!Io
zP&gnQ0{{Rp8vvaFD%Sv#06qx<fk1%*000n{mhKmJ4T-_40_kf*OpDdu$NBpEkA*tj
zZ)~rB%&MaRf&XS8Ew}SYIjs(N4OnmpMrI`4)m^Ey{(+rYa9Yb|UCDa74)R#nA=Wzy
zudM`d-YIBAH}xb`Nz+C`zIW_6g4boz?8E8wWLnK>!T76ZYEKLQmMX^XeeG{BbKXM`
zU0Dx`z3I>xjzi(s?ed(T4x(b{3PBTe`D!*~Ze%{<cCL6>>4iXxwFSN`gdF)VHwFeA
zU#g0l;7sN#B_p;Kk#x<Qgv)hr6q@VQxB&kD|I~Zv2MPjCx`Pf1WBN?ndXE~G=tSHH
zdR*oTkvz8K4wu|;m`<fvx4A!-LclMO&{ESy((MnB0>um1m9zhl@-{eT{bDCL=(b=c
z^CPYnA}k*wL<eGm2K#T`&wU$5?Jvo2Ww~SqORiCV7)m2figJM@+q!VX$uR*I+St`3
z(Z|#Jl~ElX#P>xxS;+_hBrNdCYUR7Jl!M5YafybdB!A_ufOi~AKrLrOInZZNTK6l>
zbRZOPLTU38^#wDr(Tz`|c>r=P6UXWB{c6zauhz5tC}k-MJfjD;Qlk@G4Xl=T$u%T1
zWQn=nq-U!l_{1c|%R$YqL?BT2@Su4M8%%jj-Yw|p1`o?hCOQkhc1|xE-vSTDS!_@g
zk+}uk?PCxCe6-9D5dJ=(&DvzTDXZN3vNHw5Ak^@0B;-Z}q3-Icj5)=+e1UqoTLCG&
zM8kx4mY|Saq|zJ-)lE5|Y(wMF$MmoDw49-M@mW6+8U>l8X?<^<Ml`?Rn;A%lga39e
z7VP-b8}UhJ;(%j(YSx1%nOKd*slm8MTn|Ku0F&wn=FY%qfadOB<o2R?{H%Mk$rdv3
zBsUqx^A9CB)$iKsKbZjtVJ_@TI}}q5cHtuDAzYXAqS@1|Y`4@zskx>F952pGoeI(?
zeX{lq8{RLy_vULcdxzkxV(OA2?A+i2k4_3U2^wGcdDw^9YPx9HPOj|7{#FQ<)aF|H
a^N4~<>s>Kk5d!ip0u+Q_%Gn-15C8!42g6|i

diff --git a/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-mdpi/bg_no_location_map.webp
index a6130fba782478a78b422e1e36548230d98f400a..03f9ba50626bfd180143d0386ef1fcfa7d8d344c 100644
GIT binary patch
literal 594
zcmV-Y0<Ha0Nk&FW0ssJ4MM6+kP&iCI0ssInUH}jfr-3+<WVG{@f2dxn?hZjHZre6u
zdUyX-`z@@vZQF=RUfqA&(~KiYYSUGG36wkuf<(NN)TV8~jOn>!zx@jdLP!Wi;`uVX
z3n_$zL_+w_P~L+N;o-x=hd+9U{6D9Br+!*@mDwxCoH_tlvTZAkE*8Zl@`yfO+>@UF
z;EczW>hFmDI}+HoZNLoOBeHa#@Vp-na&>w<9|zgGpM-NfNLRcT4nU}6^R2<u$Oa%N
zjBEhBbSoPGCt8Z6T_YtNfI($s1K{UcDs9`fkwiy@wQvB2)17Ppg3`zaz|Xd_0dV;(
zzD_(qYOMCmgU$o&FqjzT!Bl<h5ER@N->XsUAk*Tj$VG40ihJ0kf3{1m#dTrdE^$F_
zrUT0$G00Vjws^W`w|I>F_!2tPHuv${#c%N*+TvxyJ<Nva?c%q19lH1+7jN;A`u{9m
zfbbwM0WXi$;=2GS<S=%oDg2etARhvq`YfGE0jd0aOHOE89u+#+?V>(}t{n?GNoNWb
zC5bJ5e&wge1v&8{RC^X;%Ffi#kb{Lyn+xYd37yIKwy$|;*CKMT7LS(HIaJdJIXZ{>
zwoN&7P=gI};vAYXbSByy3ijQbamYjs7Ajn4ayvqsLk4?1@yJ=b7Bakf${sO^%{`i>
zGm+*HS!c39nL}&BL#W#ZX$}#p$$bhc+gmX;W5OI_`0alGK$$}fT1EhLImFa$batf5
gp@Qwd_j_j|YYuVocs{P@PP%=*+Vwi~=yHDo0P{c`fB*mh

literal 638
zcmV-^0)hQfNk&F?0ssJ4MM6+kP&il$0000G0001v003bC06|PpNJ0Pr00CeWNs^*b
zvta$0R52_)#{s=IA|?R*R*;He^56neGPnV0xddW7NX%0GSN&K0r#X=r`%H|+1yTeD
zZny#!-_-zCP&gnC0ssKe5dfV5DvtnS06qx<fk1&6000n{kfG^=iL8tI+*EbpH6e|3
zSf0RTEEL}BF57@eqn==ISPZiY_Al=cF_%GRL5Rrx<P;Bn9o;d0)_H2(Y&o_M^02Cm
zx}A?lBB41FP2}aJ`*g+A(i^bA?G($qLjf{6Zx0E^Kn<Z<KT*MJ=(SXi;@#qB$qAyD
z9XJ(#Fs4!O3V;Cq|No_g&}2%x#;*byBBhb<Bv1fQ$Si+C$B%TCQ4+D*Rt`YPf^Bll
zs7J6PSIn($dW$LEUtnuy&Jm{aHG7c{DUhk;Q(3VjEXecQo`vRU4$cWjvq+8sMqt_(
zr`KT$+-iH)UL=eMbJe#S;nG1P=KaS*U1m;Zs}yG$qWm)*gz|H7_hQ3HCIi<2Ak^*n
z@bUz#f}}2hp5t@tGVY>nZh@5;E;mwLsR3jPQkp}llNFKR{$w>2RiRK9G9DRy-YEQv
zNOaR3t3m!1d^zOxiGag-R><JUo)Ye)2VfNhtNSv!*Bq>@C0GVzG1!d|%8Pzw<6j9h
z7O5t`?^(bH|13XF<#R%znSSR7eHAuPa<kcmq;SMwhaRjm8z`M5Iu4!?frU-zmz1Pm
z)zBb3yI7RXO@%V3a3MhHw9kTw05TNn#{daxWBHqSaPOG~`4^{R&Atg6BkQeE!2~k4
YEO((?JeOcCc91o4;@Y(|uBt!)0E~DU`2YX_

diff --git a/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-hdpi/bg_no_location_map.webp
index 5c0d2e39d55ba843721e60c64fac277efacec6eb..76e0a75dd66e13c0a0e6f62d535c6c7c1957b76a 100644
GIT binary patch
literal 958
zcmV;v13~;!Nk&Gt0{{S5MM6+kP&iDf0{{RojzAa?N5d$RBt_X~*1xs;F)mqP&`6S^
z8Y$;r9>W8pHj*5<v~-p~t$l|JiXusB`UQq@NFgalAwd!~u>(q(p9I3Nh%l@`cu*z$
z0z%{$sftMbRT8Pc>IUEnnV>OnQ1Kq3|Ch-0@hLD8+q!O9>T<mg$d)8YaU_-0V&-LL
zW@cvIIn8RB|Nj@TRF%_qj}iTc<hG3<NuHU_b%Cs%q<22xdwaQYZK3MZoA0ga+C<g2
zN7pWTsal2`_diFoj%A?O;DucvGCfCjfyiICZ4Z7Zzg)2kMDej}yFe7`J+%u&rfJXf
z{H@}e$Kfb7?RXiUB14zng{xrKu~(@<<!#GAlwGda1)}(bV;6{m{bzQ8$h7Wx3K<nP
zJcN`|^Nv@LQ+WK+DtKmEC0UvJ9eCm-t?UZ=YqcO=#Anc(cef!RU4#cNz>CwKkaS_1
z55bGeu_#Hagl@^sVphCJ!&~x3NxXTZW9ZGh*KUZ&DwcfFAC@ls4cq9=JCPu1<<`KP
z_usO-B`2oQ7y0c5@gh8Y4qlwdTe81>58aZ-WAc`~j{es)XCF&mE6ZE*gmo-A2ye+}
z1LBr!8sRN@Qr?pDD;4abqF~8M_-S=pep-zToq-oez4C)b`#yMaJ}Pg?_3+c`d`5l;
zTd<BL$4B9JF!|65^`2nA3M4Pnv<=KA>8Q92OFd~RHRV8ZU`S7q;R`^zNmZup0M3IA
zDa&8pz+#X9l%B8Pa1|v8#c(Wnn(H3BE#kcs?mNYpxROlPT=#T9e$Z&x17L|e<hftE
zo|GRn3Tx`*nauwjG?sJn)9SRN$`-f(D!)#v_41ZHcmbD7qkk>(o$}4wdZacNGk-zz
zw^>|_MCC1cQ->bn(Zico6Y{yfq;ZrpOt_oMMuFrPd5~Ocs9}+B5+9$%_k}Q%-I^Bp
z{tz<LTnnu=S|{VMI7ub~W6E+Glx1!%$(VM10FeTdIVq#;FmP<Mb4l*TIT8k=iOGm)
z&rc_jIO4{fbnLF@YXl;f#I@IGFgP<zPF)3^_J$D{;hv3L9bJ#b)o~*1TvF3~mWO&S
z<#p%T`hX_vy+!-R*>3(sJC_mw4>bp(N)(t(L)<v0)4!sfOAg58el`CK+PMUD<7_gr
z90j<!g!ND}{DpQdVd-)-PNcfG_>b@&|IBAqbE(0zUthV7GJ(8LS6@V!<Ni+r<j<$q
gs)RwNxVeP+MJ(Uz^WA-|djI6<<KZ4ow=Zu#05ZGa=Kufz

literal 474
zcmV<00VV!YNk&G}0RRA3MM6+kP&il$0000G0002h005Ez06|PpNOJ%H00DQPG?FAW
zdf?A8ByjFOA_6FIKgv@)W(K3$ww0{k0IOgP7*oU}FxLPSummguXaJ4C%&p0t@+0~`
z0gT&!7!#3b$A>Z&q8G-ZtRGJA0#e>U=8yWH`k(s$cjE)3yMW|75IuTfEQ)r(hy})}
zKolwg09H^qAUFX401z1fodGJ=0FwYdZ8nxkBqJgrAWEQs4T))P;cWrOpaO?!&}yox
zcpq}+xjNssS+9ooEQpARcSOd?!;E^+oXWl;oyI`hW0oSFrIhoN@Q-S5d{mOnX}XPw
zjmsh;BW&MFj9=^^`6E!>+WsPrZX?Ay4OG>O*_k+EJkotE3AgfMN4D&n+q`zUHmD#u
z;uk|Amstx)&=hCN1aR$9q=V0mx5J+-zh6)W01ir&8mZDOXV83Jf5!~C8zma8*3h{k
zH&Pn~A(VB)Ztlj`V(b9^{mW@^G4b>L;6}|@%tM5ei0yc~i-8S(&&ViX3K+1=;;gfQ
z(W^vAv7oSi`S)FuTYipA&~1#wzdoQqO+q~aU+kNNfFLx|Xb|jh4X=~R$QM@PBOG1t
QFY5{{A{q<IbM!=x0KS#ek^lez

diff --git a/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-mdpi/bg_no_location_map.webp
index 3c17c090d6b58edd123695e0ae55e9bd9a60792d..79900cec1b1dca1732e09434b1691d7548be2256 100644
GIT binary patch
literal 640
zcmV-`0)PEdNk&F^0ssJ4MM6+kP&iC%0ssInT>uaecfz1;BuCDHdH%GXL+=z2wQVEk
zHyYzl>u8*&sF9?onjc>9F9#(Tqezk>CA?9B2IQjwCHJ0c+qPjwr}B<z*H41DQ$d1q
z3hp@~0uhNwDnx|JO+v5IZBa-N$xXXpe+e%>9K1WhsrBRXPZ={Z)BJYZ?}IE!woTib
z_t>^=+xy*P-P`~6aGr^H-x2+H<hG3@$+P4-fZ7S>36I-huQtc)<GNR!+fg@%z4Bxi
zx&d=?q?)gUy8l!I;Hw*|0dSVqssXTN7peg;Cr0|f67Cwxz~ikOssV5oH!AsCthqu|
ziUov(JBC_*H^)vj0IuR%m|o0G9PHVd@Gwo;h=VyfdY2uRIR|wRZ;nmH1E5+gqZu?&
z#VW0oi)G75H^+o7R$?eTEXZQLg**Q{vDyK$SVd{E`f+k|WTx81iVye7n?qi#DuY-X
z<;~%g7R%@}G{;O@EI+6g%WwlZ=8&XVP52S5&G8-prZ@{SV)f0y7JGUsKpZ$@j`!J*
z3G6$YSJoUU_#5j+*=ktX5HdekLy#`kl8YkzjcM__IpXBzNKIMTx)9>Xai>761|T3+
zvm}dU3U$ck<3kAGtD!~~i%J&|S}*PXpd=9lxA9s*4dZ4hMsEle!u4*IA&J!&<>6fC
z9Re&Y2)R8eySkg59QNQW$zFGYylm_U$vr~`aWf+oKp5{?GQ*mKag9g!Tv-<!JhZUy
zF6VVo^>7ftmId!FGCBb=L+rc0=g!aOU|ZuZi92SVvJzNe-vy!TI)7kV<F>Wbzvs7?
af35`~y%*y8yq-@lrTqDKKJF)xTy75ye=k=6

literal 320
zcmV-G0l)rINk&FE0RRA3MM6+kP&il$0000G0001v003bC06|PpNJ#(y00BRsAOIOg
zDJ=zNw?;$+-b2lUZDiX@zW-RswE&MD`tR$+5Yhh$*!n;?ykZWFgQ*~bO%y6n%HMw<
zLL!AO6n=&n%(f!bw*UZEP&gpS0000m4*;D3DvtnS06uLtmPn){A|W7)xPT3bX#f`5
z03L@p(OX6jS5M-rI+DWd#C^(KeK7ti0^PH)No>b&JLV9X{ndr_TD&3!J@^{0CW>YR
z=^ASM)j~SxRUtTMCMq^@0t>_#E>&|H>)&;!aLl1?4(-u0(=Yg5#ed}gN*SXJ0092~
zVhUpTiMi=;C=J*-$XDy|3=}-czUo=W&md^U@_65cD`-W$O4llG7n*f>u8UPPPzpc~
SY_ot9xV;7gqGuZL0002jrGMQ3

diff --git a/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xhdpi/bg_no_location_map.webp
index b79c747ac141234d4ff079f871756c0bc1b8aa8e..14f7e0e44c5ee1c210deb28a128f57f0aee8a7d3 100644
GIT binary patch
literal 1500
zcmV<21ta=WNk&H01pok7MM6+kP&iD;1pojqzCa)l|HC$tBqu!tL;k5@VZK1bz@}{^
zK~8rxjz7B7_dkx>NRlK+51rwUYrcCX+eng}^b3pp1JaRbFtIeKZQF(!oyt4ly!TIn
z_)SDG9D#WQ5+VY52tf=mfkQCCP+o={K@2285b_azw8~4)aJ<A1=cOMw<e8%#M+Ch1
zQDCENy3syl{mb%m$$zp1>lj;bUgiDPkAJT3F^mnU4FIG#b(pYmZ;6)3<60adZAinz
zuB4<5ZU?b;k%Z^&`rrNS&CFIlMf4xGB)M^$*|>-&<XjU55P!hXt7i}H_Vuir4_>?)
zdiD5DU*Ed(<kia$51oHJf7I8z9^KMBH~RnYsNT{%&ZybeIso7)?_Fpe0RQ-|)H(o;
zQM0FY07Pn~JJ2y-RG_pcwGKG^#=g`uUrL?c=xQAR*AHe}r+k|N3;TKpgnxQpYLai4
z$k)%c4uCVg)YBk}iq))v)&UV5x1`>%LGJA4nbxo+NUnFa4#03kTjm4XBmyN%Y7CoF
z8%?Qo06e4huGRq%$=a!Ius;ZuEvXYw<TZAr)&X#*{^)2O0FhW~=>-(?RraL@z?E-q
zmwn&@WVthq9`nw&A+Q6HS>k<#%Kbt>U<cp*%KQeh7K4DTK;ejYnod3G#tuB0CU5ig
z8hP}<HEMD<pMjoQL~FdwH?S%pz*eYm+~=KoC53+j<++=0JtxqEZ2gS4`F6$w6wEi^
z{p?Zp1Gd6ttIt~9<ru#C#&&p{uT@W@o6no8OV+{JuHnZw-_i;1XOF#EA7Cq5X|WCl
z-P#CxYS+XTZ}WA{B>oMQ+myUhZyDZC&8O{-g4`~;(SsOs^YuH`s1rT)_zv$Xre($)
z*nw|sYrwn=%X{!sb2s141itx3sy*gqSUD!pUyx5TZ>zPI!hmi*W0$%4w(vU&Fk-fO
z2hB<JO5T~^zU<JdMDfj6W^TUI8h%T}KfZs-JN2I#bn`h<zwvIX^_m8H5#vg7H{a<m
z5rLk%#C$?@vJk*;j*nZ+0}r4}q#GT^m;wmMoick|X{UgQ0@Zz<Y<xJ$1VxS;+%f}{
zB=Q?QZftvimfUIcZ(gSX^c1QbUGZVxM-Xs?3Wt&l<3Lm{E)$}bAIQp`sxt^=MF3@m
zY8Lr33Q8mkLG)#Z@WQbq&5}dyo8IhquM_AkPc{A`JUxkn66ueg>1?F%8=IMpbCyM-
zFhB1t|KtU1#cDKLBE5{__uBo^nDl4-jh|@2lJsag)mZyVIb2J8amuKP-A|bZC4yh;
z17Ef42{(F>AvWULbE_@vR|enGTP83eimtT@oimEB{J-s{ll#;7H&CcT#(al<H6|6>
zYDs+a6`C}Hs^TTFr!wL)N8CEt_~``IFr;E{nLu+qwS>;1Q2V_!)R!Hm{lnPiL+@)O
zK#7bU^Gj<XiX33Gu68=sZTiWZF*jST6wichjy`W>T})}HeY;|{Z=cQ&$Ne1XDb^_=
zn;WG^<p@7^+-g*8H$ZDC>0-u|U3H3`sq0z6iG8mc1)N&qe0SXI;KD^&?WXYU;jO)u
z0!%njoah^vwR3AJ=HftAv5MG2ek!e}1bKQPHL~bMXm9VD&p{hgu}J)#!t1ZyLVz;u
zq+*>yW7$l$Y6(G>hEQXb&dSp2=rB!W8kG3HLuz$$%uA7Nw_28pq_+F%QGn(UDok$O
zTb^8x2wLp>)F_hd{jVhiB@|DuMRn~h<0V5|@%&Me9RTc%dubpmYR{scEy~lc(`rn7
z1&Twcj3hn)tHtnHRXatK{s6L(_uwL9>O&~{L36_O8T5)or`czrE2|n7Se)k+hfsVL
zv8<&q5}QJeMOOb0)<}CWxTd(n)MB>#xrM-iVn=Ubpu0DKmIbxbx6kcy>yiCMqF;@9
zes3QB{5Gv2)B!hczkK!d4y7W|;I?{r=kcqdS1%vlyKST1ynC;rx36!r()|a|Ukw2g
C<oI&{

literal 1062
zcmV+>1ljviNk&E<1ONb6MM6+kP&il$0000G0000T0RY1Q06|PpNOk}K00Cb>AV8Va
z0w#E1iiij$J!ZIYTido0T`cz&JSK8Fu#ghSXycba31m9f<Bevm7t#L-0H*JcMHK?*
zeoXqXKU65K{c&j?WE3wl<~JGpPy8?b7ypa@#sA{}zw2Kz)J?{6l9BafSoWKWO~XJ%
z0APL7u;&0)P&goh0{{R}B><fPDjosE06qx<fk1&i000n{mhZ>Oo?d;dU+OLQ>z;T1
z`{E9|TgUyz=nI3HIT>r)Mi+W6k+3%rW0;c1F%!!T8rQG#t+#5{2q^uWQTsTfwK%}V
z_mpYIIxU=0`!)?hJ{GZ@^UWGgT<PBAaTV@8gr?-l3y$slYqIf5lm&o|BTY6Jn3Bga
z93p_;Y^QpfM0TV%_whQg<LPL&aYy7W9j=9!Mp4?K9yt1b7mGVrxb~z72SK=Sxq*`@
zYZ{Q1=8b)`iWTeAQMV!{2SOdx)z{xMcwp0~1pZw<3c65O*g~|>;6>ye3c`Dg_6sgg
zJ#EBSxODfy)@NrNtyXa9r91FeEuuwHIv!?OU#2_oW5%KTIG}kk>rXnygHDHXHC(K6
z+fDQKaYyMY=l}rz|Nn%9NP3m><{<Zz7vKMqVBy^Ce?3rt?Qj@vy6iWffVz8-8O;!#
zV})6t*ivk%YQ!J|H+ohdHp~;Jr4Z*fVH&jp8k-m<Z+xI&nLakK*_w`Z*irlIeKAT-
zd8^7xJqo~y2OiaEOqKv-^}GLsInk2A+2Z!{l)(u}ivz!XhPW+Glzu)ywGv;`wX_G=
z?u>u^qE&1pvyTHfNeo#)-iN#@5@+;90H9E>s-MtH5Ywldp5+xrL>_-SWwONMZH!dS
zXO?vf1Rf=Wd<@njEqOBtie-9$mNU+zd15}=jlHs-=JDFT`r37x$z{m6ZDFx`3Q_r7
zL|ftC$2(XyDt|ZnS2qJWqZ}iJ406MJR0bNdR`()%^`pUl+)~@3jwHR9TY6Qb8hI_|
z9S)}r;Y@_7D(NKZbjC-kuw&Y0G;<OiYg({$6}qFw)z++HG1hStpRD4Tdr8-N?T$i?
zc~#;<9@$%Zybq1`h|Eg3R?P$I_nWseKwRpoLDYfSFhQWjxJbx`!Q^k+1CRV;#p}p^
zsT4xrhkNvB?pc5<xPY`DwT!)ip}tabBIRCL-fR`!@A3=8Rb5jZ;EnJwsN$+Wz0rcv
zXHp#2tDVNXnqF?<$`1p3SFHQRdQ;uu7O*P<k?xtB;hoc2@H$=F)(x+x8I|$w-+Zbg
zvhlXjs1$5(c7V6sSx3SjH>>yYd}r32QCpNG%&~gnsZ0L_{_C(^t{31y<)u$x!w{!p
gE5aGbl8zg3uU7XqnVDH^io=tQQy_isIrf+U0FloJP5=M^

diff --git a/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxhdpi/bg_no_location_map.webp
index 75aa5cecc6c6feecc7168a8522d1b3f086630c60..91cb7c8eb62883a69f58015697ac9ad36d5177a1 100644
GIT binary patch
literal 2022
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
E0QbN0p8x;=

literal 1446
zcma)6`8U)H6#ovg)jVUe6v>P-*3qNPSYDQ4gvf5>*@YH#8jW>Ar6Fw?^ei(;N2FPv
zB>UE}#3W0$ESa)IF$S5jW!}7cf57|U-h1x3=X~zxb3f-?S34UU>K*{VVKL60&PHC+
z002OQK?4EmAi&bW*_JQtK*`<_3GNHzOf68JL548fp?6e#O#Z5p^qB`Xr#XqI7!IJR
zaDQtD05$$G_lJ&7Bd1Q>KQ+7a%LM`EPUVOxODnwHpkXyibHy!fZL8avsSiA(g{2oZ
z9)-y;byW<M5@r`hqxsfxoX$K=vWG@@;>vLsKOS?ndFPmbCHVg(>Fct88lD&8B*#R_
zmT<PhiYEw5wsMy;?jdroyYDz;u6aWtL4<T#)II=mN4CwrE!6e{C(Qi)=MeLg4~%ai
z0Y|iFX?tQtQ#%3>UIdmb1^~ZX02Gim9fSax9o%;d0~Z77hyj5}<b>WZtRr$ll*Z(R
zY56Zwv%P1>V&y1>EL-H{;I3PXXBDT-l+28VPf!vVBehT+5#RA18NWrVS`&VdO4ms3
z;rT2M5$*z4!$|ZHQ41sW5Y8DkDyEDzYqgXDM^Z*p@r0icHucOLHnX_Acg`zCCVwO1
zJzd#Fa--rh)uR@!@wNvFK~Dxt8FI%<h2W))UeK?`O)l1VDe(#mUh^y65FOS{iC<I9
z@XABOqwY{PdeEx$$PQ-Jx1r2(`#*|B7aMzQ@^TOHgCu$^pJwvBZDjqr&itaQsI*P5
z9;NBIUF#Z7C2p~CUwI_^MERd9jRxF&Bvlgrp7J0u?o0Wu$B`z50u=_02lp@@oxL6l
zW5p(zW+_<Kx2E0l|AYOfkR^tVU2?7Y)VM$j0UsJ>L8g`rj@>q`peLDigqdJ<No)S#
zT!@*}L~shZpu9b_4$0UKcT{$&;x_-#Z(oS=<MlpOov)qo8;geVfydMX1ke4&PT)EM
zr>?&If#_0Ct#zr&r%ls}y)o$viF!J)<Et^&rkR2g(&&|<g2IrOEt^adOGE!PcAh_a
zq4G_Pn5&zETTqp3=)Wc`ea~DcpM+Q2V%b<L5t<QQXiMX0svi{qb_FY)Q@gAKCSi-u
zi|AKrFYWQ%-~QmrUI9jG*;szlLGm}bOD5chlR9=_`_{M^VqP#_Ixul<`N=e37?3+R
zYdzcL^zpQ4Qi57hi@fz4L7&N7xBa@q6h8yRrVtiAVs<Y-c5yI_ZUU=9scMLW_;Hlu
z)nSQ}c?>x+)q`J;vshPeIp?TRcQR1YOlqJo$Rex#aX<ifwxGk`<2amm*Zs}bWL{D}
znsWcOSoE%|)=YOS(3&j>z!RwIhoKm;p&E4OKwXPSoN0ML#j537mCrNWxw-fh)K1ro
zvu_-AVermJc{7C@-O&~zmz1!s)HZR6!$#I8ygV~ERzy}C7$00a=5}A<M$9!7<5%@R
zw~|@$bEtm1-e52wCoO88l#|@^j92ZbFm&zJ-1lmJRe1CEVBe9B40LwZmnU1U1e|>;
zt^I5`a$TIdciCBPU`I7FHd?H1<R}_=SNdXRyzjIOH}c09<yhj|8nqqG!ho@CS(!YX
zx<E-kOgDA&hNVOk_1>$fv1LYbAAdSl`kd|_?cIt@T5@+8-k?7MNXmi0WX=yETXTK%
zz~B?Ip#%2X2H@agy>uVtoI?(HnHofsxYu{VOK0h+<)S5pX8Guw!ShhB%9+g;*?jKw
zN_u@O6=Arq^8UvDLV~!B;@NM;@qzQ=NmuE(!P8^59lhK6M3Zlfw0r|~TsvB<TdSIs
zyFX~?9YPT<;aE8aV`!2&vH0u#ITzKhp`d01W|N#T0WQDuqU!Sp5e1AFXY@;?nv{Or
tz-~88Bf{Nb#4=ppVE=7$Z<)xW(o70Z8#P2EDxG?M{^_s|JwW(>{{eA<+CBgP

diff --git a/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-night-xxxhdpi/bg_no_location_map.webp
index 511d237d4bf8a6637a90a936da6bb88879048023..e4864a9eb29bc936ac13ac6305ad8ec4df5cd01c 100644
GIT binary patch
literal 4010
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
JVva0&{U7bm;Y0uc

literal 1690
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
U5Au9g2c8cqzMloAQ_ukL4>k=#xBvhE

diff --git a/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xhdpi/bg_no_location_map.webp
index e908191371edda5adbee97f864fdea5d0f7eb3a8..513089b55bb6775948489034653c83e722d9f9b5 100644
GIT binary patch
literal 1320
zcmV+@1=spgNk&E>1pok7MM6+kP&iB!1pojqzCa)l&%!8@Bq`amoc;}=isy2pfl=E?
zl3cYHe_D%YpDPu^wrwNPX?y$6`<hWC*-n~lA^SIg0<H*wbIzbPZ3AXZ&mFLT_ir39
zr~}3U%6Rw#Kfd>e=hO!g289VHUhH^G-;wl9g(UFz@6xHiAT#hf_$zbz=}R^|X)#s@
z%?*u&2tx@3iNMe(9D%5iMq(7!_i8qP+O};^+r|$B6o?54sI@RLsH~Uly(gK}+eUc&
z|9_JG@QjX65&efPNp9R`HZI}`IU#~e#2>JB{_g4RTRQ8`<2UDPXV325(pz_*pS}J1
z(){D)gIl`m;T`3<ee3@ned&}3+_==8uL?7xJ1_=mt2RGq)|a{i;BA-JRR_QhbE7%{
zPLLU$`DyB;)*Z0CjZ#y7Ph*_jP#pj#%#4yCG$clM!1lK*1wm>yU*1$505|N6ZcHIR
zHM#?%|E1QB2~y9OYB6096-IRc>>xF&127IrqccB0OchEoY4MdtbpXaNGm1bI_-UgB
zbn^RBEvDQ;TLqxic#z+ejsw(z_&&1hXVihczER5#s!eYMVy))o4begKaj>WZHxhn5
zVV0pwUVO({BRXiih&-@^h480G-FxJzy}l40Bms~YUo5@&vP1eEDA<yn`g#`G<iX*l
z@Zu|{E<TOS@jCIyi*NNuDZTjC>5DI18sWkINFQw75MF%q6_6L-q7vRm_T&M0z3{-K
z0e$KN@x>PcwVW^$ej5BMrtc&D&%#p|J%gZcgcslDKwo@2o$%sI*XfJzK>GDW8F45+
z;f_MLLmv#%PlN5>K6xwe&Wr1^8{yABP-}J|{d%I?cYs>MHQ|rHJf?qsuYVR^e5R)_
zKBq5k$}YatqqjsN>DLqaA${>3X2OfFi|C6lNUsZb6t;bf+)?PoTOtfjgY!yw;L@PT
z^lfGV13AGbv4IJIiTp(=bRB<&#!rv-0658x<W1j}Bk+<V?&%Q$Kl!3Y(5n@2lrPBL
z-v*{~Vj;d@vjVnqFL$i~5@4-?|HzUVa0I(EdQT^<c$4BXkJ&A=xI}M>{J9j{rwr|P
zr@<roV4ttCnOiv0AG?`7`qLoW>Dv&wY1BXIXO^)}(;A(F`%aTzO>5BGY8SUlS$&FI
z{e~|%k{90rvk~;l!O{O#OCMgKcNDUg5k2x(RQ~47Rpc@lJY>^H)*g<?{B~czO<#Pg
zMZu&`!DV)G+_VOL@okq3QX3yogvc-X<7M)|zM9eZDS#u#o8t%c<0icOlwgV{Yq3q;
z<CFUu$gMJ+$`|xTv9$Y0yK%B@&3p_jp%f#I&y$+tp(BzAdFxkA+_A;3JPH@bHpSqf
zEsTDuCUMHt$smemh9f2$SsLY#AUCp(YzNCCM+nBpam5gh510MQnhwUkhLq`B0euQx
z>R_VOTzw5I<uQHhaOMVb^?CS-Z4s~d(z&>G!RMNE!)MC%2zi1u%%*QSQaU(xn0cQc
z_icV`V|@*F|4oJIBboXd*I7>A&REgoUQWk#c-0@-V32Fb_v-6fU@;*@+9nz28;m0Q
zWoq_)3diD$U3#(+w{GztI$8KOEp15UYw+4aNm<%B5-Ed+2M!$(a+Nayk<Z7kuxk>b
z7xs_AHp_buQ31tqrb4c<es2K1!1@~E-oE`Ivp(gjk;ao(cX|05xwyA(e{=TYE+<pz
eSlsK`+1mM=NB8eesP`wQD1EpqO81_=K3@acV3tJy

literal 1228
zcmV;-1T*_mNk&G*1ONb6MM6+kP&il$0000G0000U0RY1Q06|PpNQ3|Y00D4YZQFC)
zmC|`@d5j$P`~Tmo%uHg0OzY_A7$FbvRNjb~0D%7R^3>yYS>vbqQ<ZLj?Vh^a8u(M0
zPVK49?noTTRvhc6I3MA^@L%{Z{1^TU|Aqg;f8oFIU-&Ql_pjIeB#u=gjzL`<1zZ{f
zu&pq5dB6s;@EkuYlkH88*Ylta09H^qAQ}V!0PrLLodGHz0mJ}42?BvYfdK#j5SEti
z7i$h~=Y7OGLM?nqVqUKPKhLM;yaCryfHsKsZ!gHMcUXV@;bH)KAS|GPfF}J}X<zy~
zk~htGD6I&HBLCHvmH(pPA6#y1`XwnE*Pw@wnDcIAW|EV8KqQnV&OF>O$4pKKXWS|%
zJlgCQTOJxcZR~_`urFmQLM2*Kzx8F2KCakTRXlS*TAAcRWsB|=6dr0RksGxo4tnoj
zl8xUH%lg(b(!bk-O7EBP80~V;K}L1%;n<`i;}`w1(!cXQ-XX}m(V_-&n!)km{dvM$
z+zr5i`yPMlWI3^Dk~!qkvN!^!u(el+fT)hsxI+OHpN}B5%mEFvG=gc)-zF6TC(y3_
zlu&uMDy<FK1yvB4buD^NBQwD)0092~|I?ER{J&h_sP#aUKS#&?N^e&j?Y%o-8h)`(
z2DnqtT!Og%c{2-Ge=7{6S6N>guwLsJGz(F&nEFE+pWs)hX#fykYr9_h!wCatz}%50
zqAS)<CX1Rw(@@v>UMOJ%fc$+n#LQ`@n*^8mlBzi5DOU{`^eylJltAkF$E9=hXaaL<
zBgjy)QE1+MWPtj}O2+(@Mk-woI~-J|mw$-`^~pK}G;dD~ooYi4_f&C#*<Oqw0O#kF
zj3Txp43qj=8DZsm*A)`Ps90{cTK)f;Uj{=ES<)eUI+!l3#=+eQ@ylfju1Fk$sLgHY
zSO9>Xo|0b*8l7I!d8JVzr@?0^PRAf7Szl)6Rtv}2#0!M`N_4g@7j#S0?$YX9?^)AJ
zx&6X9fB;1o_RAQ1wHzV<E=6k_I=Ggb&ic|kL5cN>)J&ty_Ic#wXaXt&hnJjGB&9>l
z`l}Z=C~LLY-azwjs%Pct{-kt5R?pz5Qn*NZ9K598$3>svZ^Q3HyI6d_fJj@b`B9&b
znq8<<nMe{-vm9($?v}sHtqhteTO{V)Q?Iixy>j;jraXP9+34GY@|#jUqZo7OM(D*)
z?oLTmctLRt=(8VybR_~ko1K`XP$SJOra6wm-~61cE=z!isED0RgCD8kK7PFSMA6_>
z(1;L$?WH^1Uf@D6Bm9qP7To|={IGkbtjq7&h%Z3eEq3Aw@}E<<o~J%VT!Z{rR80z5
zqU-p1d*1+T!<6?rwgT`#ogL_f2*OZL-e2vsE)P&&m&NzwGO;}^o*@-$Ggu>7TL}Un
zJci)YetXH|5ncBB?hcw1o*4XMB3iWOY=;+p9WYdpi}v535oA!Z8MCpB8E7$E?tuLx
zlnQr56I7^dFxqLne6me{-t(o?hLCRzq=^A{$Tp~`p|Fs~$8H0g{l-G;M<wGC)-?tU
q2GDL`A-tI6lgz>;{ar6&w9Kf4q$8ewess4!EuIZ^*AI3mL7)J)H&gBa

diff --git a/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxhdpi/bg_no_location_map.webp
index e062178367e8a1a5733d9a9f6c1bea55474df305..50284965a78ab3ded0bd60f0b5621258acdaed59 100644
GIT binary patch
literal 1822
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
Ms5=jyrc$XC0DNU^N&o-=

literal 1846
zcma)&c{tk#7{`AJinQ)CBw{pZQ}>kCoe*k{bgsB&WsYaA#!;G5aTjwJ)0I}Dj;hog
zwI+21t+RF1F$o$s#dLK<9hq&<{@)+(^Zny}zMuE~e!snj#cJ>afU6}M=Yli9LjV8}
zJ)JrbfCK^Nwm5492mpjg9?Ta%F2%BDHsdD2H*3t~*-?zg;%M|4Xw5f!Gcz;sAbF;k
zHfVIW+uyjfM9e7_>>#plzjlQ@PPHKSkvqa;!`*5RBd=z*7V$lhkU(YZ)iQfmL##!K
ze$C%WXONxIZufe-lY5pV>y#8X$<6N9mB?qn{Gp)rBQSosCCPeZ&85K_3U?E$$o>g(
z?7$u4?wI~>;m(a_^-dY!bY5<E%i<bJ3@L(Y<saV6gK<V>`#qM`M#R>Owp6&1eX*Yp
zSm}3GRpNcm<p_KQ{<kJ~Clbr3-FlKdc2J`hgu&ETkGCt0IAh{9C+X7AEbJyqu80bp
zHmA7C2LRg`pcHhG3Q_@?2!a#L5_C?>EGpWPxh1mR7kgxyL%Zv#iULHiA<iN%zZe}V
zG<)rz3`9i-H@al3M7eU=X#@xCiq~6uY4p{t(C^O#mTXYu<d@e2)m95fP-IsNYPy$r
z6EdZAx*`kGHAU3Fx)sIx_;S({7_K3m)5(iTV8;9MisX*x*CJhPo)x-D#^L_(i0F*D
zl}0fLaywd3LyFUokv4zR&;``~&y1~+gm5mc>ezlL^I!Vu`pRfZzOD&sz0L0mN+~wZ
zgb@7QKaSJ;&$ub>!DtW^l{rdPXk86>F(38cU&1C7dZL__L)mjNSbwV1XIbF2Pu`ev
zrIl@ViF^Y{d%~_xUc(IkH2W2+hS!t&OWfrzAv^CfCzS-@WTsGX6etIuk9l-%$Qg32
z{9hMb)_xGtgNn~Beuw;A|0#7XPhi8if2%R{hi)c<{}c<CM(_RnKCv;DW2j6_f@0`m
zW%K}E#b|#g&mGY?!i3E;u!_~Wlq|j2{7-d|bgfY*OVN;;U24Qm(1ChF%UBp5sp;xm
z;Q`q?Ega75WeZK~gt}59s4LLH5DU!#(yYj_v3zMXtRhT&WJiko$asgzER#?m&PwVX
z=j;8Q95ZnAOPns-uf!GCH`P*+{!idk&Ww~g(a|1iSlj5qH%hwx$#LKU|G_eQ=eBxM
zU9E8-0Gx1-|0FL{%C%+iecV3;f|m(Lw{(RTyH#f%T>2o~K?RgoYko8|wfR-5mMgdM
znliu!nG<D|m)T=6Z#IqY1~@sGAJ#1BgQlEk(9;Df44=6}JA`A8w%JDOO+Zk%wSBbG
zTI}~w=bTqMC$L$bNYFD|-X^?0-|W5ewD*MFX6Nl>5lESs%*nPv>_N6$oULlY*Mbka
z0XS<Dag7-!Elzr8IQv`LP3*{p-d^jo$vC2e%X*W0JRzas6Ta55Q#nGn0K*THh@6lC
zR3gNzs!Bud@mQ1!q-48~+Lfxdw+5+f*O#x)?tv}u1Pp4lVQJw~<-_XaI;7!nbh`EJ
z1pwj?Q0?fYA!+AH1mjA*^N;u|)K+#Y120}DT<&Dsr*uS$uT<4ex?ITP@Ey_8PtKA+
z6gpAyDMu&lRbvK;`(6R9-QF*JL(~eLmoHr2obEn@*?Dp!#M_I-iNvu<09ppCJ1Ap^
zlrE?T7H?D??<1P7XsuO-q=eDO&j-9frm@vkQ;%}@-4Dv@>~Mg9my@XAp_|qKO-e=`
zde+W=X!<4x_HY+N&`%ru2n=tfl=XNI&WEhg%=el(^GCe}=DzQ9E@<A5<k&F2Pw>@p
z$;Ho^efzR!T)Cmrb}rC7%zi=8UsEmf_1)YwXtA!6ViKh>COB@`%a{Gps%=~%s?RHQ
zw7gcA464u1g~b-qD0UoArSYKamklL1i=ZXhp+4ELI_<B5*s5<}Q)dXbA#c&0Mp@Hx
z0mKIB4EGPx`j0bY6Hx12a5nZ!pqNf_rl1>$*0MDBfWfJLNPaRf=*L0c|1(@x?;BiR
zQ=x1$OGigQE2!3S^;e{XIM!QKO6Yvd$kY9@kMV)L8{O87LEdvT#2kM@>qF7xu9ZEi
zJN(Vp7nejDL!oU<1kR{F$#f8zGtD%;EZ@^nEf@CskWuZ+hoTBIa#$yZW3#Nqr~Kr}
zTTs2Fp<<G(vi#5@E(oR?JPziz!ZfizD87w;I6>#lR;^fjveJBMr#{t6yl&PYQ$0Jv
zXJJTNQS)bTL-CzNw{`jFbCk`(_G-EN1F8y*M#aURmUF?cqJ9FNzm4N-;Vnd7`i$pk
z^@faV(BpbGO+;E~?U{km%z28Vf$91?E8gb_W2gRf0gHZh)2@A2*WRoZCQh2M*fy&(
z&>rt)M!mDnbFkd7>38hOz2VPe`FQ1n2i|*$ZJ*m3-Z<f_@#1b5U6I*=xb{_NfjBXd
ZF}LH;mJ6rzWP)uVYFNp=Vl>JO_zNz(f_VS{

diff --git a/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp b/vector/src/main/res/drawable-xxxhdpi/bg_no_location_map.webp
index 8b110d33fe17e3323d75c6dda9eb29ee4f0bc8e3..881af0055a73c371b926c46db9540cdf39c1c1c8 100644
GIT binary patch
literal 3522
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

literal 2706
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
F_%|y?E0h2L


From 4a2310954acf5fa36e429f76c896b1210d48ce0a Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 16 May 2022 10:11:14 +0200
Subject: [PATCH 38/45] Removing non necessary LiveLocationAggregationProcessor
 interface

---
 .../sdk/internal/session/SessionModule.kt     |  5 -
 ...DefaultLiveLocationAggregationProcessor.kt | 99 -------------------
 .../LiveLocationAggregationProcessor.kt       | 78 +++++++++++++--
 3 files changed, 68 insertions(+), 114 deletions(-)
 delete mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
index 0aae9f3105..dab6061042 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt
@@ -88,8 +88,6 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
 import org.matrix.android.sdk.internal.session.openid.DefaultOpenIdService
 import org.matrix.android.sdk.internal.session.permalinks.DefaultPermalinkService
 import org.matrix.android.sdk.internal.session.room.EventRelationsAggregationProcessor
-import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.DefaultLiveLocationAggregationProcessor
-import org.matrix.android.sdk.internal.session.room.aggregation.livelocation.LiveLocationAggregationProcessor
 import org.matrix.android.sdk.internal.session.room.create.RoomCreateEventProcessor
 import org.matrix.android.sdk.internal.session.room.prune.RedactionEventProcessor
 import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProcessor
@@ -392,7 +390,4 @@ internal abstract class SessionModule {
 
     @Binds
     abstract fun bindEventSenderProcessor(processor: EventSenderProcessorCoroutine): EventSenderProcessor
-
-    @Binds
-    abstract fun bindLiveLocationAggregationProcessor(processor: DefaultLiveLocationAggregationProcessor): LiveLocationAggregationProcessor
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
deleted file mode 100644
index 3ac47ee8cf..0000000000
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/DefaultLiveLocationAggregationProcessor.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright 2022 The Matrix.org Foundation C.I.C.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
-
-import io.realm.Realm
-import org.matrix.android.sdk.api.extensions.orTrue
-import org.matrix.android.sdk.api.session.events.model.Event
-import org.matrix.android.sdk.api.session.events.model.toContent
-import org.matrix.android.sdk.api.session.events.model.toModel
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
-import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
-import org.matrix.android.sdk.internal.database.mapper.ContentMapper
-import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
-import org.matrix.android.sdk.internal.database.query.getOrCreate
-import timber.log.Timber
-import javax.inject.Inject
-
-internal class DefaultLiveLocationAggregationProcessor @Inject constructor() : LiveLocationAggregationProcessor {
-
-    override fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
-        if (event.senderId.isNullOrEmpty() || isLocalEcho) {
-            return
-        }
-
-        val targetEventId = if (content.isLive.orTrue()) {
-            event.eventId
-        } else {
-            // when live is set to false, we use the id of the event that should have been replaced
-            event.unsignedData?.replacesState
-        }
-
-        if (targetEventId.isNullOrEmpty()) {
-            Timber.w("no target event id found for the beacon content")
-            return
-        }
-
-        val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
-                realm = realm,
-                roomId = roomId,
-                eventId = targetEventId
-        )
-
-        Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
-
-        aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
-        aggregatedSummary.isActive = content.isLive
-    }
-
-    override fun handleBeaconLocationData(
-            realm: Realm,
-            event: Event,
-            content: MessageBeaconLocationDataContent,
-            roomId: String,
-            relatedEventId: String?,
-            isLocalEcho: Boolean
-    ) {
-        if (event.senderId.isNullOrEmpty() || isLocalEcho) {
-            return
-        }
-
-        if (relatedEventId.isNullOrEmpty()) {
-            Timber.w("no related event id found for the live location content")
-            return
-        }
-
-        val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
-                realm = realm,
-                roomId = roomId,
-                eventId = relatedEventId
-        )
-        val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
-        val currentLocationTimestamp = ContentMapper
-                .map(aggregatedSummary.lastLocationContent)
-                .toModel<MessageBeaconLocationDataContent>()
-                ?.getBestTimestampMillis()
-                ?: 0
-
-        if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
-            Timber.d("updating last location of the summary of id=$relatedEventId")
-            aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
-        }
-    }
-
-    private fun Long.isMoreRecentThan(timestamp: Long) = this > timestamp
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
index d2450aef9c..76b7a4ec8e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/aggregation/livelocation/LiveLocationAggregationProcessor.kt
@@ -17,18 +17,48 @@
 package org.matrix.android.sdk.internal.session.room.aggregation.livelocation
 
 import io.realm.Realm
+import org.matrix.android.sdk.api.extensions.orTrue
 import org.matrix.android.sdk.api.session.events.model.Event
+import org.matrix.android.sdk.api.session.events.model.toContent
+import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
+import org.matrix.android.sdk.internal.database.mapper.ContentMapper
+import org.matrix.android.sdk.internal.database.model.livelocation.LiveLocationShareAggregatedSummaryEntity
+import org.matrix.android.sdk.internal.database.query.getOrCreate
+import timber.log.Timber
+import javax.inject.Inject
 
-internal interface LiveLocationAggregationProcessor {
-    fun handleBeaconInfo(
-            realm: Realm,
-            event: Event,
-            content: MessageBeaconInfoContent,
-            roomId: String,
-            isLocalEcho: Boolean,
-    )
+internal class LiveLocationAggregationProcessor @Inject constructor() {
+
+    fun handleBeaconInfo(realm: Realm, event: Event, content: MessageBeaconInfoContent, roomId: String, isLocalEcho: Boolean) {
+        if (event.senderId.isNullOrEmpty() || isLocalEcho) {
+            return
+        }
+
+        val targetEventId = if (content.isLive.orTrue()) {
+            event.eventId
+        } else {
+            // when live is set to false, we use the id of the event that should have been replaced
+            event.unsignedData?.replacesState
+        }
+
+        if (targetEventId.isNullOrEmpty()) {
+            Timber.w("no target event id found for the beacon content")
+            return
+        }
+
+        val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
+                realm = realm,
+                roomId = roomId,
+                eventId = targetEventId
+        )
+
+        Timber.d("updating summary of id=$targetEventId with isLive=${content.isLive}")
+
+        aggregatedSummary.endOfLiveTimestampMillis = content.getBestTimestampMillis()?.let { it + (content.timeout ?: 0) }
+        aggregatedSummary.isActive = content.isLive
+    }
 
     fun handleBeaconLocationData(
             realm: Realm,
@@ -36,6 +66,34 @@ internal interface LiveLocationAggregationProcessor {
             content: MessageBeaconLocationDataContent,
             roomId: String,
             relatedEventId: String?,
-            isLocalEcho: Boolean,
-    )
+            isLocalEcho: Boolean
+    ) {
+        if (event.senderId.isNullOrEmpty() || isLocalEcho) {
+            return
+        }
+
+        if (relatedEventId.isNullOrEmpty()) {
+            Timber.w("no related event id found for the live location content")
+            return
+        }
+
+        val aggregatedSummary = LiveLocationShareAggregatedSummaryEntity.getOrCreate(
+                realm = realm,
+                roomId = roomId,
+                eventId = relatedEventId
+        )
+        val updatedLocationTimestamp = content.getBestTimestampMillis() ?: 0
+        val currentLocationTimestamp = ContentMapper
+                .map(aggregatedSummary.lastLocationContent)
+                .toModel<MessageBeaconLocationDataContent>()
+                ?.getBestTimestampMillis()
+                ?: 0
+
+        if (updatedLocationTimestamp.isMoreRecentThan(currentLocationTimestamp)) {
+            Timber.d("updating last location of the summary of id=$relatedEventId")
+            aggregatedSummary.lastLocationContent = ContentMapper.map(content.toContent())
+        }
+    }
+
+    private fun Long.isMoreRecentThan(timestamp: Long) = this > timestamp
 }

From 2aeee79c63bdc78421254e8db630ccfbb1fcb873 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 16 May 2022 10:14:39 +0200
Subject: [PATCH 39/45] Using existing common When case for NoticeItem to
 handle location data

---
 .../home/room/detail/timeline/factory/TimelineItemFactory.kt  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
index 9b6026031e..07ae9d66c3 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/TimelineItemFactory.kt
@@ -113,13 +113,13 @@ class TimelineItemFactory @Inject constructor(
                     EventType.CALL_NEGOTIATE,
                     EventType.REACTION,
                     in EventType.POLL_RESPONSE,
-                    in EventType.POLL_END             -> noticeItemFactory.create(params)
+                    in EventType.POLL_END,
+                    in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
                     // Calls
                     EventType.CALL_INVITE,
                     EventType.CALL_HANGUP,
                     EventType.CALL_REJECT,
                     EventType.CALL_ANSWER             -> callItemFactory.create(params)
-                    in EventType.BEACON_LOCATION_DATA -> noticeItemFactory.create(params)
                     // Crypto
                     EventType.ENCRYPTED               -> {
                         if (event.root.isRedacted()) {

From b0773514eedfb002bc29bcdb9440e749fb507101 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 16 May 2022 10:32:34 +0200
Subject: [PATCH 40/45] Extracting LocationLiveMessageBannerViewState into
 separated file

---
 .../live/LocationLiveMessagBannerViewState.kt | 36 +++++++++++++++++++
 .../live/LocationLiveMessageBannerView.kt     | 19 ----------
 2 files changed, 36 insertions(+), 19 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt

diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt
new file mode 100644
index 0000000000..976085386b
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2022 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.app.features.location.live
+
+sealed class LocationLiveMessageBannerViewState(
+        open val bottomStartCornerRadiusInDp: Float,
+        open val bottomEndCornerRadiusInDp: Float,
+) {
+
+    data class Emitter(
+            override val bottomStartCornerRadiusInDp: Float,
+            override val bottomEndCornerRadiusInDp: Float,
+            val remainingTimeInMillis: Long,
+            val isStopButtonCenteredVertically: Boolean
+    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+
+    data class Watcher(
+            override val bottomStartCornerRadiusInDp: Float,
+            override val bottomEndCornerRadiusInDp: Float,
+            val formattedLocalTimeOfEndOfLive: String,
+    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
+}
diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
index bb69cdd1c2..8cb552e3c4 100644
--- a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
+++ b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerView.kt
@@ -35,25 +35,6 @@ import im.vector.app.databinding.ViewLocationLiveMessageBannerBinding
 import im.vector.app.features.themes.ThemeUtils
 import org.threeten.bp.Duration
 
-sealed class LocationLiveMessageBannerViewState(
-        open val bottomStartCornerRadiusInDp: Float,
-        open val bottomEndCornerRadiusInDp: Float,
-) {
-
-    data class Emitter(
-            override val bottomStartCornerRadiusInDp: Float,
-            override val bottomEndCornerRadiusInDp: Float,
-            val remainingTimeInMillis: Long,
-            val isStopButtonCenteredVertically: Boolean
-    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
-
-    data class Watcher(
-            override val bottomStartCornerRadiusInDp: Float,
-            override val bottomEndCornerRadiusInDp: Float,
-            val formattedLocalTimeOfEndOfLive: String,
-    ) : LocationLiveMessageBannerViewState(bottomStartCornerRadiusInDp, bottomEndCornerRadiusInDp)
-}
-
 private const val REMAINING_TIME_COUNTER_INTERVAL_IN_MS = 1000L
 
 class LocationLiveMessageBannerView @JvmOverloads constructor(

From 3d136112b23ddad07ff0bc9165b852e76b677994 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 16 May 2022 10:39:46 +0200
Subject: [PATCH 41/45] Fix filename

---
 ...agBannerViewState.kt => LocationLiveMessageBannerViewState.kt} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename vector/src/main/java/im/vector/app/features/location/live/{LocationLiveMessagBannerViewState.kt => LocationLiveMessageBannerViewState.kt} (100%)

diff --git a/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt b/vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerViewState.kt
similarity index 100%
rename from vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessagBannerViewState.kt
rename to vector/src/main/java/im/vector/app/features/location/live/LocationLiveMessageBannerViewState.kt

From 1d6083b2c7d19ff2c2da739a844fcc926358dda9 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Mon, 16 May 2022 18:07:08 +0200
Subject: [PATCH 42/45] Fix missing dot at end of code documentation

---
 .../main/java/im/vector/app/features/location/LocationData.kt   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vector/src/main/java/im/vector/app/features/location/LocationData.kt b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
index 2393edb249..b3466ff871 100644
--- a/vector/src/main/java/im/vector/app/features/location/LocationData.kt
+++ b/vector/src/main/java/im/vector/app/features/location/LocationData.kt
@@ -38,7 +38,7 @@ fun MessageLocationContent.toLocationData(): LocationData? {
 }
 
 /**
- * Creates location data from a geoUri String
+ * Creates location data from a geoUri String.
  * "geo:40.05,29.24;30" -> LocationData(40.05, 29.24, 30)
  * @return location data or null if geo uri is null or not valid
  */

From cbf8306c6fe90907078278689b518230cfead989 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Tue, 17 May 2022 15:09:27 +0200
Subject: [PATCH 43/45] Removing live location share data from
 MessageInformationData structure

---
 .../LiveLocationShareMessageItemFactory.kt    | 21 +++++++++++++++++--
 .../timeline/factory/MessageItemFactory.kt    |  7 +------
 .../helper/MessageInformationDataFactory.kt   | 12 -----------
 .../timeline/item/MessageInformationData.kt   |  8 -------
 4 files changed, 20 insertions(+), 28 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
index f993676958..479a742369 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/LiveLocationShareMessageItemFactory.kt
@@ -24,7 +24,6 @@ import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvide
 import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
 import im.vector.app.features.home.room.detail.timeline.helper.TimelineMediaSizeProvider
 import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
-import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationInactiveItem
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationInactiveItem_
 import im.vector.app.features.home.room.detail.timeline.item.MessageLiveLocationItem
@@ -36,6 +35,7 @@ import im.vector.app.features.location.UrlMapProvider
 import im.vector.app.features.location.toLocationData
 import org.matrix.android.sdk.api.extensions.orFalse
 import org.matrix.android.sdk.api.session.Session
+import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.threeten.bp.LocalDateTime
 import timber.log.Timber
 import javax.inject.Inject
@@ -51,10 +51,11 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
 ) {
 
     fun create(
-            liveLocationShareSummaryData: LiveLocationShareSummaryData?,
+            event: TimelineEvent,
             highlight: Boolean,
             attributes: AbsMessageItem.Attributes,
     ): VectorEpoxyModel<*>? {
+        val liveLocationShareSummaryData = getLiveLocationShareSummaryData(event)
         val item = when (val currentState = getViewState(liveLocationShareSummaryData)) {
             LiveLocationShareViewState.Inactive   -> buildInactiveItem(highlight, attributes)
             LiveLocationShareViewState.Loading    -> buildLoadingItem(highlight, attributes)
@@ -150,6 +151,22 @@ class LiveLocationShareMessageItemFactory @Inject constructor(
         return liveLocationShareSummaryData.endOfLiveTimestampMillis?.let { DateProvider.toLocalDateTime(timestamp = it) }
     }
 
+    private fun getLiveLocationShareSummaryData(event: TimelineEvent): LiveLocationShareSummaryData? {
+        return event.annotations?.liveLocationShareAggregatedSummary?.let { summary ->
+            LiveLocationShareSummaryData(
+                    isActive = summary.isActive,
+                    endOfLiveTimestampMillis = summary.endOfLiveTimestampMillis,
+                    lastGeoUri = summary.lastLocationDataContent?.getBestLocationInfo()?.geoUri
+            )
+        }
+    }
+
+    private data class LiveLocationShareSummaryData(
+            val isActive: Boolean?,
+            val endOfLiveTimestampMillis: Long?,
+            val lastGeoUri: String?,
+    )
+
     private sealed class LiveLocationShareViewState {
         object Loading : LiveLocationShareViewState()
         data class Running(val lastGeoUri: String, val endOfLiveDateTime: LocalDateTime?) : LiveLocationShareViewState()
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
index ea703a0d11..13f783cded 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt
@@ -216,12 +216,7 @@ class MessageItemFactory @Inject constructor(
                     buildMessageTextItem(messageContent.body, false, informationData, highlight, callback, attributes)
                 }
             }
-            is MessageBeaconInfoContent          ->
-                liveLocationShareMessageItemFactory.create(
-                        informationData.liveLocationShareSummaryData,
-                        highlight,
-                        attributes
-                )
+            is MessageBeaconInfoContent          -> liveLocationShareMessageItemFactory.create(params.event, highlight, attributes)
             else                                 -> buildNotHandledMessageItem(messageContent, informationData, highlight, callback, attributes)
         }
         return messageItem?.apply {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
index e06a983ab3..7874f843e1 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/helper/MessageInformationDataFactory.kt
@@ -21,7 +21,6 @@ import im.vector.app.core.date.VectorDateFormatter
 import im.vector.app.core.extensions.localDateTime
 import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
 import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
-import im.vector.app.features.home.room.detail.timeline.item.LiveLocationShareSummaryData
 import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
 import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
 import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
@@ -120,7 +119,6 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
                 isLastFromThisSender = isLastFromThisSender,
                 e2eDecoration = e2eDecoration,
                 sendStateDecoration = sendStateDecoration,
-                liveLocationShareSummaryData = getLiveLocationShareSummaryData(event)
         )
     }
 
@@ -189,16 +187,6 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
         }
     }
 
-    private fun getLiveLocationShareSummaryData(event: TimelineEvent): LiveLocationShareSummaryData? {
-        return event.annotations?.liveLocationShareAggregatedSummary?.let { summary ->
-            LiveLocationShareSummaryData(
-                    isActive = summary.isActive,
-                    endOfLiveTimestampMillis = summary.endOfLiveTimestampMillis,
-                    lastGeoUri = summary.lastLocationDataContent?.getBestLocationInfo()?.geoUri
-            )
-        }
-    }
-
     /**
      * Tiles type message never show the sender information (like verification request), so we should repeat it for next message
      * even if same sender.
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
index be0f2d55ca..258424c7de 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt
@@ -43,7 +43,6 @@ data class MessageInformationData(
         val sendStateDecoration: SendStateDecoration = SendStateDecoration.NONE,
         val isFirstFromThisSender: Boolean = false,
         val isLastFromThisSender: Boolean = false,
-        val liveLocationShareSummaryData: LiveLocationShareSummaryData? = null,
 ) : Parcelable {
 
     val matrixItem: MatrixItem
@@ -99,13 +98,6 @@ data class PollVoteSummaryData(
         val percentage: Double = 0.0
 ) : Parcelable
 
-@Parcelize
-data class LiveLocationShareSummaryData(
-        val isActive: Boolean?,
-        val endOfLiveTimestampMillis: Long?,
-        val lastGeoUri: String?,
-) : Parcelable
-
 enum class E2EDecoration {
     NONE,
     WARN_IN_CLEAR,

From e9d93194f15f58c3ce10e132b6fc244de4c8f5bb Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 18 May 2022 09:48:34 +0200
Subject: [PATCH 44/45] Adding comments on some strings and removing non
 necessary plural

---
 .../main/java/im/vector/app/core/utils/TextUtils.kt   |  2 +-
 .../detail/timeline/item/MessageLiveLocationItem.kt   |  1 -
 vector/src/main/res/values/strings.xml                | 11 ++++++-----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
index 80dabd5021..d2f8c4022b 100644
--- a/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/TextUtils.kt
@@ -118,7 +118,7 @@ object TextUtils {
 
     private fun appendHours(context: Context, builder: StringBuilder, hours: Int) {
         builder.append(hours)
-        builder.append(context.resources.getQuantityString(R.plurals.time_unit_hour_short, hours))
+        builder.append(context.resources.getString(R.string.time_unit_hour_short))
     }
 
     private fun appendMinutes(context: Context, builder: StringBuilder, minutes: Int) {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
index daca48e7aa..838fbd46de 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageLiveLocationItem.kt
@@ -57,7 +57,6 @@ abstract class MessageLiveLocationItem : AbsMessageLocationItem<MessageLiveLocat
         holder.locationLiveMessageBanner.stopButton.setOnClickListener {
             attributes.callback?.onTimelineItemAction(RoomDetailAction.StopLiveLocationSharing)
         }
-        // TODO adjust Copyright map placement if needed
     }
 
     private fun buildViewState(
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 93955e54b6..1851e33d7f 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -322,12 +322,11 @@
     <string name="start_chatting">Start Chatting</string>
     <string name="spaces">Spaces</string>
 
-    <!-- Time units -->
-    <plurals name="time_unit_hour_short">
-        <item quantity="one">hour</item>
-        <item quantity="other">hours</item>
-    </plurals>
+    <!-- Time unit for hour: if a short version exists, it should be used -->
+    <string name="time_unit_hour_short">h</string>
+    <!-- Time unit for minute: if a short version exists, it should be used -->
     <string name="time_unit_minute_short">min</string>
+    <!-- Time unit for second: if a short version exists, it should be used -->
     <string name="time_unit_second_short">sec</string>
 
     <!-- Permissions denied forever -->
@@ -3024,8 +3023,10 @@
     <string name="location_share_live_started">Loading live location…</string>
     <string name="location_share_live_ended">Live location ended</string>
     <string name="location_share_live_view">View live location</string>
+    <!-- Examples of usage: Live until 5:42 PM/Live until 17:42-->
     <string name="location_share_live_until">Live until %1$s</string>
     <string name="location_share_live_stop">Stop</string>
+    <!-- Examples of usage: 6h 15min 30sec left/15min 30sec left/30 sec left-->
     <string name="location_share_live_remaining_time">%1$s left</string>
     <string name="live_location_sharing_notification_title">${app_name} Live Location</string>
     <string name="live_location_sharing_notification_description">Location sharing is in progress</string>

From 4ebd1ea138af6e0208b46439c3c873596aa2ba16 Mon Sep 17 00:00:00 2001
From: Maxime NATUREL <maxime.naturel@niji.fr>
Date: Wed, 18 May 2022 13:58:42 +0200
Subject: [PATCH 45/45] Fix parsing of location data in non encrypted room

---
 .../sdk/api/session/events/model/Event.kt     | 11 ++++----
 .../EventRelationsAggregationProcessor.kt     | 27 ++++++++++++-------
 2 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
index 16bdbd3432..7124d8a1a3 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/events/model/Event.kt
@@ -26,6 +26,7 @@ import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
 import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
 import org.matrix.android.sdk.api.session.room.model.Membership
 import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
+import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageContent
 import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
 import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
@@ -375,11 +376,11 @@ fun Event.getRelationContent(): RelationDefaultContent? {
         content.toModel<EncryptedEventContent>()?.relatesTo
     } else {
         content.toModel<MessageContent>()?.relatesTo ?: run {
-            // Special case to handle stickers, while there is only a local msgtype for stickers
-            if (getClearType() == EventType.STICKER) {
-                getClearContent().toModel<MessageStickerContent>()?.relatesTo
-            } else {
-                null
+            // Special cases when there is only a local msgtype for some event types
+            when (getClearType()) {
+                EventType.STICKER                 -> getClearContent().toModel<MessageStickerContent>()?.relatesTo
+                in EventType.BEACON_LOCATION_DATA -> getClearContent().toModel<MessageBeaconLocationDataContent>()?.relatesTo
+                else                              -> null
             }
         }
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
index 6cb76e9fbb..af9c0071fe 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/EventRelationsAggregationProcessor.kt
@@ -193,16 +193,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
                                 }
                             }
                             in EventType.BEACON_LOCATION_DATA -> {
-                                event.getClearContent().toModel<MessageBeaconLocationDataContent>(catchError = true)?.let {
-                                    liveLocationAggregationProcessor.handleBeaconLocationData(
-                                            realm,
-                                            event,
-                                            it,
-                                            roomId,
-                                            event.getRelationContent()?.eventId,
-                                            isLocalEcho
-                                    )
-                                }
+                                handleBeaconLocationData(event, realm, roomId, isLocalEcho)
                             }
                         }
                     } else if (encryptedEventContent?.relatesTo?.type == RelationType.ANNOTATION) {
@@ -267,6 +258,9 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
                         liveLocationAggregationProcessor.handleBeaconInfo(realm, event, it, roomId, isLocalEcho)
                     }
                 }
+                in EventType.BEACON_LOCATION_DATA -> {
+                    handleBeaconLocationData(event, realm, roomId, isLocalEcho)
+                }
                 else                                -> Timber.v("UnHandled event ${event.eventId}")
             }
         } catch (t: Throwable) {
@@ -763,4 +757,17 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
             verifSummary.sourceEvents.add(event.eventId)
         }
     }
+
+    private fun handleBeaconLocationData(event: Event, realm: Realm, roomId: String, isLocalEcho: Boolean) {
+        event.getClearContent().toModel<MessageBeaconLocationDataContent>(catchError = true)?.let {
+            liveLocationAggregationProcessor.handleBeaconLocationData(
+                    realm,
+                    event,
+                    it,
+                    roomId,
+                    event.getRelationContent()?.eventId,
+                    isLocalEcho
+            )
+        }
+    }
 }